Architecture
Compile-time rules, CI gates, automated enforcement.
Architecture decisions are encoded as compiler errors and CI checks. A rule violation fails the build.
Six sections: code analyzers, CI guards, financial bookkeeping, GDPR deletion, search sync, and local orchestration.
Catching bugs before they compile
50 custom code analyzers
These are custom rules written as Roslyn analyzers. They run inside the compiler and flag violations as build errors that stop the build. Here are three examples:
The messaging outbox handles the save. Doing it manually breaks the atomic guarantee. events can be lost.
await _dbContext.SaveChangesAsync(); // Outbox commits automatically
// Mutations via EF tracked entities // MassTransit outbox commits on success
A new ID on each retry means the provider sees each attempt as a different request. causing duplicate charges.
policy.ExecuteAsync(() => {
var key = Guid.NewGuid(); // new per retry!
});var key = Guid.NewGuid();
policy.ExecuteAsync(() => {
Call(key); // same across retries
});A hardcoded "USD" breaks for customers in other countries. Currency must come from configuration.
var amount = new Money(100, "USD"); // What about GBP, EUR customers?
var amount = new Money( 100, options.DefaultCurrency ); // From config
Blocking bad merges automatically
159 rules checked on every pull request
Beyond compiler checks, CI runs 159 regex patterns and 12 assembly-level rules against every PR. A single violation blocks the merge. These catch the mistakes that are correct C# but architecturally wrong:
Tracking every penny
Double-entry bookkeeping for payments
Every payment is recorded as two entries. a debit and a credit — just like real accounting. The database enforces that they always sum to zero. If they don't, the transaction is rejected.
Deleting user data across 8 services
GDPR right-to-be-forgotten
When a user requests data deletion, a saga coordinates erasure across every service that holds their information. It tracks progress, detects stalls, and produces an audit trail, all within the 7-day legal deadline.
Completes within 7 days · stall detection at 24h · audit records anonymized, not deleted
Keeping search in sync without polling
Database changes stream to search automatically
When a product is updated in Postgres, the change appears in Elasticsearch within seconds. The application does not write to two places. Instead, Postgres's own change log feeds the pipeline:
No dual-write risk. The database change log is the single source of truth.
Testing across service boundaries
13 test suites, from unit to end-to-end
Unit tests verify business logic. Integration tests run against real Postgres and RabbitMQ in containers. Contract tests (Pact) ensure two services agree on message format before deploy. All run in CI on every PR.
Running the whole platform locally
One command starts everything
.NET Aspire orchestrates all 8 services and their infrastructure (Postgres, RabbitMQ, Redis, Kafka) with a single command. No Docker Compose file to maintain, no manual startup order.
$ dotnet run --project AppHost 13 infrastructure containers 8 microservices 1 command Dashboard: http://localhost:15888
Want to see these patterns in action?
Try the interactive demos →