The Monolith and the Swarm
DShop exists as one deployable and as nine services built from the same requirements. Read side by side, the two builds show which decisions were decisions and which were defaults.
Newest first, grouped by year. Search or filter by topic.
DShop exists as one deployable and as nine services built from the same requirements. Read side by side, the two builds show which decisions were decisions and which were defaults.
Nine DShop services share one skeleton stamped out by hand - and the places they quietly disagree teach more than the places they match. A drift catalogue read from the real source. Part 2 of Nine Services and a Message Bus.
The anatomy of ArchiveKit's SaveChangesInterceptor - detecting soft-deleted entities in the change tracker, building archive rows with zero extra database reads, and the one line of state-flip ordering that keeps EF from cascade-deleting the archive you just wrote. Part 2 of the Archive-on-Delete with ArchiveKit series.
Rename, move, add, remove, type change - VersionKit's attribute surface covers five kinds of schema evolution. This part walks the whole declarative API, the merge rules behind stacked attributes, and what the attributes deliberately refuse to express. Part 2 of the Entity Versioning with VersionKit series.
One naming class in DShop.Common decides every exchange, routing key and queue in the estate - the message topology is a string-formatting problem.
Header, route, subdomain or query string: how the platform decides who is asking before it touches any data, how tenant identity becomes a connection string and a schema, and where the caching knives are hidden.
DShop.Common is the shared library nine microservices were built on - and the rough draft of a framework its authors later shipped as Convey.
Hard deletes destroy the audit trail; soft deletes clutter live tables forever. Introducing ArchiveKit, a .NET library that intercepts deletions and stores immutable archive snapshots with TTL policies and cascade archiving.
Rename a property, change a type, ship the release - and every payload you stored last year stops deserialising. Introducing VersionKit, a .NET library that declares schema history as attributes and migrates old data on read.
Database-per-tenant, schema-per-tenant, or packed into a shared database? Kicking off a series on building a multi-tenant platform where one EF Core codebase supports all three isolation strategies behind a single control plane.
A practical walkthrough of the pipeline that ships this very website: from push to production on Azure App Service, with secrets kept out of the repository and zero manual steps.
Four pipelines run dotnet test against projects with no tests and pass every time, the shared model ships as committed .nupkg files in a folder, one executable has no pipeline at all, and a DLL with no source hides an algorithm pasted inline elsewhere.
Lessons from migrating a multi-tier .NET Framework monolith to .NET microservices on Azure Kubernetes Service: what to split first, what to leave alone, and the manifests that hold it together.
SaveChanges is already a transaction, until it isn't enough. A tour of explicit transactions, TransactionScope, execution strategies and the failure modes that only show up in production.
UTC timers, IST badge gates, DateTime.Now in one executable and UtcNow in the next, columns that record neither - how a system whose entire product is timestamps got away with never deciding what time it is.
Every five minutes a function hunts for people still virtually swiped in past ten hours and emails them - magic node numbers, an at-most-once flag with a gap in it, and a local config file pointing straight at production.
A 22-character job name, a 20-character column, and an error handler that has thrown on every attempt since the day it shipped - plus the four parallel logging systems that still could not catch it.
When one job needs another to redo work, it leaves a row - an action enum, a rewind date and a Done flag standing in for a message broker, plus the delete-and-replay proc that makes reruns possible.
Four magic strings in an audit column decide which rows the batch jobs may overwrite and which belong to humans - provenance, workflow state and inter-job protocol smuggled into a field meant for names.
The shift calculator writes one report row per employee per day since 2019, inventing blank rows for days nobody badged - a dense date spine built from sentinel values, at the price of one database round-trip per row.
Badge gates record when you arrived, not which shift you worked - so the platform infers it, scoring every candidate shift by L1 distance in time-of-day space and letting the smallest number win.
Leave records arrive late, change and get cancelled - so the sync reaches back into already-approved attendance rows and re-derives them, carefully stepping around human decisions and allowance money.
The leave sync resumes from wherever its own job log last said Success - one table serving as log, watermark and metrics store at once, with recursive pagination on top and an envelope check in exactly the wrong place.
A daily Azure Function mirrors the HR system's employee master into the attendance database - a field-by-field upsert, a clever delegation rule, a vestigial handshake, and the parse calls that can kill the whole run.
Five executables, zero queues - how an attendance platform coordinated a web app, three Azure Functions and a console job through one SQL database, and what that choice bought and cost. Part 1 of the series.