Skip to content

feat: scaffold Movements service with full CLEAN architecture#3

Closed
ericburcham wants to merge 48 commits into
mainfrom
feature/movements-service
Closed

feat: scaffold Movements service with full CLEAN architecture#3
ericburcham wants to merge 48 commits into
mainfrom
feature/movements-service

Conversation

@ericburcham
Copy link
Copy Markdown
Owner

Summary

  • Scaffold the Movements SOR service following the Contracts reference implementation with full CLEAN architecture (Api, Application, Domain, Infrastructure, Migrations, Tests)
  • Add Movement aggregate root with state machine (Scheduled → InTransit → Delivered → Confirmed/Rejected), NServiceBus HandleScheduleMovement handler, MediatR ConfirmMovementCommand, and query handlers
  • Add K8S manifests (deployment, service, migration job), nginx ingress route, and Tiltfile integration (port 5003)

Test plan

  • Solution builds with 0 warnings, 0 errors (StyleCop clean)
  • 16 unit tests passing (domain state transitions, NServiceBus handler, MediatR handler)
  • Integration migration test passes with TestContainers
  • tilt up deploys movements-api and movements-migrations successfully
  • GET /api/movements/ returns empty list
  • POST a nomination triggers ScheduleMovement command → creates movement via NServiceBus handler
  • PUT /api/movements/{id}/confirm confirms movement and publishes MovementConfirmed event
  • OpenAPI docs accessible at /api/movements/openapi

🤖 Generated with Claude Code

Eric Burcham and others added 30 commits March 10, 2026 09:25
Define architecture principles, technology stack, coding standards,
NServiceBus conventions, and service creation guidelines for MidstreamHub.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Set up Directory.Build.props, Directory.Packages.props, nuget.config,
StyleCop ruleset, empty .slnx solution, and directory scaffolding for
Contracts, Nominations, Shared packages, Web UI, K8S manifests, and Nuke build.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Convert solution from .slnx to .sln format for Nuke compatibility.
Add Nuke build with targets: Clean, Restore, Compile, Test,
IntegrationTest, Pack, DockerBuild. Default chain runs
Clean → Restore → Compile → Test. Fix TFM to net10.0 for
Nuke.Common 10.1.0 compatibility.

Also adds .claude/settings.local.json to .gitignore and updates
all .slnx references to .sln across config files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nfrastructure.Common)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Counterparty/Product demoted from AggregateRoot to EntityBase
- Added missing NuGet packages (MediatR.Extensions.Autofac.DependencyInjection, Microsoft.Data.SqlClient)
- Added Autofac + Infrastructure.Common refs to Application .csproj
- Added parameterless constructors to shared Volume/UnitOfMeasure VOs
- Fixed counterparty lookup to use GetAllAsync (not just active)
- Added ProductNotFound test case
- Fixed K8S port mapping (container 8080, Service 5001->8080)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ors to shared VOs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… Product entities + domain tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…idators + unit tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ations, repositories, Autofac module

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…module, NServiceBus

Add the Contracts API host project with:
- Autofac DI composition via ContractsModule (root) and ContractsDomainModule
- NServiceBus endpoint with RabbitMQ transport and SQL persistence
- Minimal API endpoints for contracts, products, and counterparties
- Global exception handler with RFC 7807 ProblemDetails responses
- ConversationId middleware and Swagger for development
- Fix NuGet package names: NServiceBus.RabbitMQ (was Transport.RabbitMQ),
  add NServiceBus.Extensions.Hosting for UseNServiceBus host integration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds migrations, migration runner, integration tests, K8S manifests,
and wires all 7 projects into the solution. 17/17 unit tests passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ContractType validation

Remove the Application project reference from Infrastructure.csproj (CLEAN
architecture: Infrastructure depends on Domain, not Application). Add enum
validation for ContractType in CreateContractCommandValidator to prevent
unhandled ArgumentException for invalid values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add proper exit code handling and progress logging to the migrations
runner for reliable K8S job execution.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ract

Add idempotent ContractsDataSeeder that runs on API startup to seed
reference NGL products (C2–C5+), counterparties, and a sample supply
contract. Seeder is registered via Autofac and invoked before endpoints.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add proxy root cert to Dockerfiles for nuget.org SSL trust
- Copy local-nuget feed into Docker build context
- Remove --no-restore from dotnet publish to avoid stale cache
- Add init container to migration Job for DB creation and readiness wait
- Fix Tiltfile: remove premature nominations/web-ui refs, fix non-workload resources
- Add NServiceBus SystemJsonSerializer (required in v9)
- Document Docker & Tilt conventions in CLAUDE.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use correct .NET config key separator (`:` not `__`) for RabbitMQ
host/port lookup — .NET normalizes env var `__` to `:` in IConfiguration,
so the lookup was returning null and falling back to localhost.

Also add TCP readiness probe to RabbitMQ deployment for startup ordering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add the complete Nominations service following the Contracts reference
implementation patterns: CLEAN architecture with Domain, Application,
Infrastructure, Api, Migrations, and Tests layers.

- Domain: Nomination aggregate root with state machine (Submitted → Scheduled → InProgress → Completed/Rejected)
- Application: CreateNominationCommand with contract validation via HTTP API, NServiceBus event publishing
- Infrastructure: EF Core DbContext (nominations schema), repository, ContractsApiClient
- Api: Minimal API endpoints (GET /, GET /{id}, POST /), NServiceBus + RabbitMQ config
- Migrations: FluentMigrator schema with nominations.Nominations table
- Tests: 18 unit tests (domain state transitions, handler, validator)
- K8S: Deployment, Service, and migration Job with wait-for-db init container
- Tilt: Added to ACTIVE_SERVICES with proper docker/certs and local-nuget support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each migration runner now uses a custom IVersionTableMetaData that places
the VersionInfo table in its own schema (contracts/nominations) instead of
dbo, preventing collisions when multiple runners target the same database.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enterprise Products-branded dark theme with sidebar navigation, pipeline
visualization dashboard, contracts CRUD with forms/modals, nominations
submission with saga notification, and placeholder pages for movements
and accounting. Includes typed API client, k8s manifest, and Tilt config.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Ingress resource required an Ingress Controller that was never deployed,
causing all frontend API calls to fail silently. This replaces it with a plain
nginx:alpine reverse proxy using upstream blocks, health probes, and proper
Tilt resource dependencies to ensure backends are ready before nginx starts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Eric Burcham and others added 18 commits March 10, 2026 23:03
Fix Tilt port-forwards to target container port 8080 instead of host ports,
add private parameterless constructor to ProductCode for EF Core materialization,
and make Vite proxy target configurable via VITE_API_TARGET env var for K8S.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ache-busting

- Fix nginx trailing-slash redirects leaking internal K8S hostnames to browser
  by removing trailing slashes from location/proxy_pass directives
- Add Vite proxy autoRewrite to rewrite redirect Location headers
- Rename VITE_API_TARGET to API_PROXY_TARGET (VITE_ prefix leaks to client)
- Fix contract type dropdown to match backend enum (Supply/Transport/Exchange)
- Add Cache-Control headers to nginx API locations to prevent stale 301 caching
- Update CLAUDE.md, skills, agents, and script.md with lessons learned:
  nginx trailing-slash convention, Tilt port-forward mapping, EF Core private
  constructors, Vite env var naming, frontend-backend enum alignment

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract fetch logic into a reusable callback and poll every 5 seconds
so the dashboard stays current without manual refresh. Subsequent polls
update silently without triggering the loading spinner.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a three-job CI pipeline: build-and-test (all branches), publish
NuGet packages to GitHub Packages (main/develop), and Docker image
builds (main/develop/release).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolves Node.js 20 deprecation warning by upgrading checkout to v6,
setup-dotnet to v5, and setup-node to v6.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add GitHub Actions CI workflow
Alpine-based Node images don't trust the corporate proxy root cert,
causing npm ci to fail. Inject the cert into Alpine's CA bundle and
set NODE_EXTRA_CA_CERTS in both build and dev stages. Also add
docker/certs to the Tiltfile only filter for the web-ui build.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Swashbuckle 7.2.0 didn't support .NET 10, so Swagger UI returned
nothing. Upgrade to 10.1.5 (and Microsoft.AspNetCore.OpenApi to
10.0.4), simplify AddSwaggerGen calls to avoid removed OpenApiInfo
type, and remove the dev-only gate so OpenAPI docs are always served.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename the Swagger UI route prefix and JSON endpoint template
from /swagger to /openapi to avoid the defunct Swagger branding.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Document the Node/Alpine corporate proxy cert workaround (manual CA
bundle approach), split cert instructions by base image type, and add
OpenAPI conventions for Swashbuckle v10 route configuration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sync the demo script with all changes made during end-to-end testing:
- Solution structure: fix skills list, k8s filenames, add docker/certs, Dockerfile.web, shared packages
- CLAUDE.md section: add Docker/Tilt, Nginx, Vite, and OpenAPI conventions
- Replace non-existent hooks config with actual enabledPlugins settings
- Fix /swagger references to /openapi throughout prompts and demo narration
- Update NuGet package versions appendix to match Directory.Packages.props
- Add corporate proxy cert handling to Dockerfile instructions
- Add MidstreamHub.Nominations.Messages shared package

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace ProcessTasks.StartProcess with DockerTasks.DockerBuild so that
Nuke's [LogErrorAsStandard] attribute handles Docker's stderr output
correctly. Also skip services whose source directories don't exist yet.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
End-to-end verification, OpenAPI fixes, and CI improvements
Add the Movements SOR service following the Contracts reference implementation:

- Domain: Movement aggregate root with state machine (Scheduled → InTransit →
  Delivered → Confirmed/Rejected), value objects (ContractReference, ProductCode,
  Volume), and repository interface
- Application: NServiceBus HandleScheduleMovement handler, MediatR
  ConfirmMovementCommand with FluentValidation, GetMovementById/List queries,
  IContractsApiClient for cross-service hydration
- Infrastructure: EF Core DbContext (movements schema), entity configuration
  with owned value objects, MovementRepository, ContractsApiClient HTTP client
- Api: Minimal API endpoints (GET /, GET /{id}, PUT /{id}/confirm), Autofac
  module-per-layer, NServiceBus endpoint, OpenAPI at /openapi
- Migrations: FluentMigrator M0001 with schema isolation, MovementsVersionTableMetaData
- Tests: 16 unit tests (domain transitions, NServiceBus handler, MediatR handler)
  and integration migration test with TestContainers
- K8S: Deployment + Service (port 5003), migration Job with init container
- Tilt: Added to ACTIVE_SERVICES, nginx upstream + location block

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ericburcham ericburcham deleted the feature/movements-service branch March 11, 2026 19:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant