Cartography of an Estate
The retrospective that closes three series on DShop - the whole map drawn once, the services wired into nothing, the skew across seventeen repos, and what reading someone else's estate end to end actually teaches.
Newest first, grouped by year. Search or filter by topic.
The retrospective that closes three series on DShop - the whole map drawn once, the services wired into nothing, the skew across seventeen repos, and what reading someone else's estate end to end actually teaches.
FastEndpoints binds a single request DTO from JSON, form, route, query, claims, headers, cookies and permissions - in that order, with compiled setters cached per DTO type. Reading the binder explains every binding surprise I have ever hit. Part 4 of FastEndpoints in Depth.
A retrospective on DShop.Common - the frozen shared-contracts tombstone, a release candidate pinned forever, and a concordance of what Convey kept, renamed, and fixed.
Tenants are data too. The tenant-management database is the one single-tenant database in the whole platform, and its five entities — Tenant, TenantConnection, TenantFeature, DatabaseGroup, TenantMigrationHistory — carry every decision the rest of the series depends on.
Each concern in DShop.Common builds a throwaway DI container just to read config - nine per service, atop three coexisting container frameworks and a vendored config parser.
Above raw resolution sits a convention layer - an abstract endpoint base class that derives its Key Vault lookup key from its own type name, a template method separating the call from the interpretation, and a generic proxy that routes by name across every endpoint the DI container knows. Part 4 of Service Discovery Without a Service Mesh.
DShop.Common makes a stateless JWT revocable with a Redis deny-list and a per-request round-trip - plus an iat claim in milliseconds and two validators that disagree.
DShop.Blazor is a Blazor 0.7 preview app whose naive choices - component classes as singletons, a silent auto-signup, a shared HttpClient - all work because of the runtime they run on. A study in patterns that are only correct by accident.
An annotated tour of the JSON document Pactify produces - the serializer settings that shape it, the snake_case field inherited from Pact v1, and the unusual decision to ship the consumer's matching options inside the contract itself.
DShop.Common's error middleware maps every exception to HTTP 400, computes a safe message, then throws it away and serialises the raw exception text to the caller.
How ArchiveKit wires itself up - the [Archivable] attribute's three knobs, an assembly scanner that fails fast at startup instead of quietly at delete time, the static registry behind every lookup, and the AddArchiveKit configuration surface. Plus an honest note about the source generator that doesn't exist yet. Part 5 of the Archive-on-Delete with ArchiveKit series.
How FeedR's quotes feed decouples a microsecond HTTP endpoint from an hours-long price generator: System.Threading.Channels as a command queue, marker records as the protocol, and an Interlocked flag for idempotent start/stop. Part 3 of the FeedR deep dive.
Convey's CQRS layer is small enough to read in one sitting — commands, queries and events as empty interfaces, Scrutor scans to find handlers, and a scope-per-dispatch decision with consequences worth knowing before production.
DShop.Common stamps a server-generated Guid into an immutable command by reflecting into its compiler-generated backing field - immutability the type system only pretends to enforce.
Splitting a name field, converting a string to a bool, deriving a flag from a total - some migrations are code, not metadata. This part covers VersionKit's three escape hatches and the sharp edges each one carries. Part 5 of the Entity Versioning with VersionKit series.
One DShop service targets a different framework, references a frozen NuGet package, and calls an API that no longer exists in the repo - so its Debug configuration cannot compile, and it only builds in Release. Part 11 of Nine Services and a Message Bus.
Correlation is the part of the saga pattern that fails silently. SagaId, SagaContext, the Empty-context trap that starts a new saga per message, and the one-method override that routes messages by their own content.
DShop.Common's Consul registry tracks which service instances it has used to spread load evenly - then registers as transient, discarding that memory on nearly every request.
Every FastEndpoints route is mapped as a Minimal API whose handler returns the same singleton IResult - and that one weird trick is the door into the whole per-request pipeline. Part 3 of FastEndpoints in Depth.
DShop.Common picks client-side discovery, a router, or a static address from one word in JSON - and self-registers each service into Consul with a heartbeat lease.
A signup click becomes a real PostgreSQL database or schema in seconds. Running CREATE DATABASE from application code feels illegal until you contain it — identifier validation, quoted DDL, idempotent steps, and a status machine that admits when provisioning fails.
Azure Functions want their key in an x-functions-key header; Logic App workflows want a SAS signature in the query string. How a discovery record's type field became a calling-convention discriminator, and how one small HTTP service hid that difference from every caller in the estate. Part 3 of Service Discovery Without a Service Mesh.
A reusable try-catch-finally wrapper in DShop.Common awaits a null-conditional delegate - so the success path throws a NullReferenceException the failure path avoids.
A walk through Pactify's PactMaker fluent API - the two-interface builder trick that hides Build() from you, why the consumer test always passes, and the uninitialized-object surprise inside WithBody<T>().
DShop.Common ships its own in-process command and query dispatch - marker interfaces, a handler triple, and one dynamic double-dispatch standing in for a library.