The Buy Path That Never Worked
The DShop monolith was abandoned with a NotImplementedException sitting directly in the order path. Reading its honest ledger is a masterclass in what an unfinished codebase confesses about itself.
Every abandoned codebase leaves a confession, and the DShop monolith's is unusually direct: you cannot place an order in it, and you never could. Not “it has a subtle bug in the order path” — the order path calls a method whose entire body is throw new NotImplementedException(). The monolith was fifteen commits and died in June 2018, and it died with its most important feature wired to a method the author never filled in. This part reads the monolith's honest ledger, because an unfinished system tells you things a finished one hides — chiefly, which parts the author considered done and which they were still arguing with themselves about.
The exception in the order path
Placing an order goes through CreateOrderHandler. Read it, then read what it calls:
public async Task HandleAsync(CreateOrder command)
{
var cart = await _cartService.GetAsync(command.CustomerId);
var productIds = cart.Items.Select(i => i.ProductId);
var products = await _productsService.GetAsync(productIds); // <-- here
var totalAmount = products.Sum(p =>
{
var quantity = cart.Items.FirstOrDefault(i => i.ProductId == p.Id).Quantity;
return quantity * p.Price;
});
var orderNumber = new Random().Next();
await _ordersService.CreateAsync(command.Id, command.CustomerId,
orderNumber, productIds, totalAmount, "USD");
}
The handler reads the cart, then asks the products service to hydrate the cart's product ids into full products so it can price them. That call — _productsService.GetAsync(productIds) — is the whole ballgame, and here is the method it lands in:
public async Task<IEnumerable<ProductDto>> GetAsync(IEnumerable<Guid> ids)
{
throw new NotImplementedException();
}
There is no way around it. Every order goes through CreateOrderHandler, CreateOrderHandler calls GetAsync(ids), and GetAsync(ids) throws. Placing an order in the DShop monolith has never once succeeded, from the first commit to the last. The single-product GetAsync(Guid id) overload right above it is fully implemented; the batch overload the order path actually needs is a stub. BrowseAsync — the product listing — is the same stub. The author built the pieces a demo would touch first (create a product, get one product) and left the pieces the buy-loop needs as NotImplementedException, then stopped.
There is a small extra tell. GetAsync(ids) is declared async but has no await — the compiler emits warning CS1998 for exactly this shape. The stub was written in a hurry, from the interface signature, as a placeholder to make the project compile, and the plan to come back never happened.
The design debate left in the code
The best line in the whole estate is not a bug; it is a comment that isn't a comment. In ProductsService.UpdateAsync, when a product can't be found:
if (product == null)
{
throw new ServiceException("Maybe we shall introduce some maybe pattern or derived exceptions?");
}
That string is passed where an error code belongs. Someone was mid-thought — should a missing product be an option type? a derived exception? — and instead of resolving the debate or leaving a // TODO, they committed the debate itself as the exception's payload. It is honest in a way polished code never is: it shows you the author standing at a fork, unsure, shipping the uncertainty. If a scanner or a client ever surfaced that Code, a user would read the team's internal API-design argument as their error message. Comments are one thing; this is a design discussion leaking into a runtime string.
The smaller scars around it
Once you know the buy path never ran, the other rough edges read as evidence for the same story — that this code was never exercised end to end:
Order numbers by dice roll.
var orderNumber = new Random().Next();— a freshRandomper order (so two orders in the same tick can seed identically), producing a 31-bit int with no uniqueness guarantee. In a system that had ever placed a second order, a collision would have surfaced. It never did, because the line after it throws.Currency by fiat. The order is created with a hard-coded
"USD". No customer, product or configuration carries a currency; the shop is single-currency by accident, and the accident is spelled in a string literal.A cart update that NREs.
Cart.UpdateProductis meant to swap a product already in the cart. Its guard is inverted just enough to hurt:public void UpdateProduct(Product product) { var item = GetCartItem(product.Id); if (item != null) _items.Remove(item); _items.Add(CartItem.Create(product, item.Quantity)); // item may be null }If the product isn't already in the cart,
itemis null, theifis skipped, and the very next line dereferencesitem.Quantity. The method throws precisely in the case its name suggests it should handle gracefully. Like the rest, it survives only because nothing calls it on a real cart.The error middleware that says too much. The monolith's
ErrorHandlerMiddlewarecomputes a sanitisedmessage, then serialisesexception.Messageanyway and returns 400 for everything — the same defect the distributed framework ships and that Series 1 dissects. Since the order path throwsNotImplementedException, the buy button — had a frontend ever pressed it — would have returned400 { "message": "The method or operation is not implemented." }.
To be fair, and the lesson
To be fair to the monolith: it is a teaching artefact that was superseded before completion. It predates most of the microservices work; the author moved on to the distributed build and the monolith became a fossil, not a product. And it is, in places, the most rigorous code in the estate — real aggregates, a UserFactory, a UniqueEmailSpecification, an order state machine. Judging it as shipped software misreads what it is. The distributed build eventually made ordering work, spread across Orders, Products and Operations with a saga; the monolith is the sketch that never got its checkout coloured in.
But there is a durable lesson in reading it, and it is about how unfinished code confesses. A NotImplementedException on a hot path is not a bug to fix; it is a load-bearing statement that the path has never been run. The order handler compiled, it passed code review (such as there was), it looked plausible — and it was structurally incapable of succeeding. Compilation and plausibility are not evidence of execution. The only evidence of execution is execution, and the surest way to find the NotImplementedException in someone else's estate is to trace the one feature the README promises and see whether anything actually walks the whole path. In DShop's monolith, nothing did.
Next, we cross to the build that did place orders — and meet the front door that made it possible: one shop, two front doors, where reads and writes leave the gateway by different exits.