cpnucleo

Learning Lab

Cpnucleo is a practical school for application engineering. Start with a working baseline, change one variable, observe the behavior, and explain the tradeoff before introducing another tool.

Learning paths

Level Path Observable result
Beginner Start the tiny lab; sign in; complete one CRUD cycle; inspect a request and its DTO A record travels from native form to endpoint to PostgreSQL and back
Intermediate Compare EF Core, explicit Dapper and generic UoW; run transport parity and rollback tests Different implementations preserve the same contract
Intermediate Modify an assignment date/hour rule; exercise it through both transports Invalid state is rejected by domain behavior
Advanced Run stale-write, SSE, performance and restore exercises Failure boundaries and operational costs become measurable

Maturity labels

  • Working baseline: authenticated CRUD, Astro/native TypeScript, hashing, bounded paging, soft deletion, schema migrations and immutable deployment.
  • Verified examples: transactional project batches, version-aware project writes, duplicate-login contention, HTTP/gRPC parity and cross-instance SSE convergence.
  • Incremental pilot: Application-layer sharing currently begins with project creation. Other transports still show direct orchestration deliberately.
  • Exercises: tenant isolation, durable background jobs, outbox delivery and Native AOT are not advertised as implemented guarantees.

1. Follow one request

Trace project creation from CrudPage.astro through crud-controller.ts, webapi-client.ts, the REST endpoint, Application/Features/Projects/CreateProject, ProjectCreateStore, and PostgreSQL. Then read it through gRPC.

dotnet test tests/WebApi.Integration.Tests/ --filter FullyQualifiedName~RestCreate_GrpcReadAndRemove

Expected: both transports agree on identity; removal makes the project unavailable while preserving the row and parent relationship. Compare this with an EF-backed resource in CrudContractTests.

2. Observe atomicity and concurrency

dotnet test tests/WebApi.Integration.Tests/ --filter FullyQualifiedName~MixedValidityBatch
dotnet test tests/WebApi.Integration.Tests/ --filter FullyQualifiedName~StaleProjectEdit

Expected: a batch containing a missing ID changes nothing. Two clients editing the same project with one observed version produce one successful write and one conflict.

Temporarily remove the transaction or expected-version predicate on a learning branch. Observe the regression, then restore it. Existing clients that omit expectedVersion intentionally retain last-write-wins compatibility; the native UI supplies it for project edits.

3. Distinguish authentication from authorization

Run Security.Unit.Tests and the UserAdministration integration scenario. A valid identity is sufficient for shared-workspace operations, but user administration needs an admin claim. Refresh checks an active account, recalculates admin privileges and preserves an eight-hour original-session boundary.

Tenant claims are informational today. A genuine tenant-isolation exercise must carry tenant identity through EF filters, Dapper predicates, foreign-key ownership and authorization tests. Merely adding a claim is not isolation.

4. Study replicated real-time delivery

dotnet test tests/WebApi.Integration.Tests/ --filter FullyQualifiedName~Sse_Refreshes

Expected: an external database write reaches the stream within the next 15-second refresh cycle. Same-process writes notify immediately. This is bounded convergence, not durable event delivery. Compare its query cost with a future PostgreSQL LISTEN/NOTIFY experiment; account for missed notifications and reconnects before claiming stronger guarantees.

5. Measure persistence

dotnet run --project labs/PersistenceLab -c Release -- --rows 2000 --iterations 50

The experiment creates a disposable PostgreSQL container, warms both approaches, and measures equivalent ordered 25-row-plus-count reads using EF Core and Dapper. Change dataset size and iteration count. Record runtime, architecture and timings together. These are local educational measurements, not universal performance claims.

6. Recover data and inspect traces

With the local lab running:

bash scripts/lab-restore.sh
docker compose -f compose.lab.yaml --profile observability up -d

The restore exercise creates a fresh temporary database inside the lab container, restores a logical dump, compares record counts, and removes only that temporary database. The source lab remains intact.

Open Grafana at localhost:3000. Perform an API request and correlate the HTTP trace, SQL activity, logs and service-instance labels. Compare Debug and optimized Release. EventSourceSupport and HTTP activity propagation stay enabled in the release configuration so the experiment remains observable.

7. Durable appointment reminders: isolated outbox experiment

dotnet run --project labs/OutboxLab -c Release

The experiment saves an appointment and its outgoing event in one transaction, lets competing workers claim rows, injects a crash after simulated delivery, and retries with an idempotency key. It verifies one delivery effect and no pending event. Everything runs in a disposable database; no real notification is sent.

Delivery is at-least-once, not magically exactly-once. The simulated provider commits independently of the worker and deduplicates its effect. Extend the experiment with retry schedules, poison messages and an actual provider only after defining those contracts. This lab is not a deployed reminder service.

Decisions and contribution rhythm

See docs/adr/0001-learning-baseline.md. Keep meaningful technology comparisons, share domain rules, and label experiments honestly. Run focused tests first, then the required architecture and integration checks. Merge through a PR after CI passes.