One Database, Eleven String Literals
Four physical Mongo databases collapse into one when Trill is rebuilt as a monolith - and the only thing keeping the modules apart afterwards is a private const string copied into eleven files.
Newest first, grouped by year. Search or filter by topic.
Four physical Mongo databases collapse into one when Trill is rebuilt as a monolith - and the only thing keeping the modules apart afterwards is a private const string copied into eleven files.
An honest retrospective on Trill's five services and its client - twelve xunit scaffolds with no test files and no project references, a green test script over nothing, and a defect list where almost every entry would have died to a four-line assertion.
Trill's entire advertising product resolves to one boolean on somebody else's entity - a published ad is a Stories-service story with Highlighted set true - and the flag is priced at a hundred units a day while the same field is free on the public endpoint.
Trill's entire ranking engine is thirty-nine lines of OrderByDescending().Take(10) over counters that only ever increase - no time window, no decay, no visibility filter - so once the top ten stabilises it can never change again.
Trill's User constructor stores the name trimmed but not lower-cased, and the repository looks it up lower-cased. Any account with a capital letter in its name can never be found, which means it can never sign in - and the test fixture is all lowercase.
Trill's Stories service ships a complete caller-identity abstraction - context, factory, claims, an IsAdmin flag - registers it in the container, and injects it into nothing, while every endpoint reads the user id out of the request body instead.
Trill's browse endpoint loads every rating document for every story on the page into process memory and sums them in LINQ - while the same codebase already has a server-side SumAsync that returns the number without moving a single document.
Trill creates one compound index on its ratings collection, keyed userId then storyId, and every read query in the service filters on storyId alone - so the only index that exists cannot serve the only queries that run.
Trill wraps every command handler in logging, metrics, tracing and an outbox using Scrutor's TryDecorate - and the whole arrangement only works because a single attribute keeps the decorators out of the assembly scan that registers handlers.
Trill's last commit built a complete domain-event pipeline - dispatcher, mapper, handlers - and migrated one of the two command handlers onto it. The other still constructs its aggregate directly, so one domain event is never raised and a reputation gate can never fire.
Trill extracts story-text validation into an IStoryTextFactory and leaves the value object's constructor public - plus an implicit conversion from string, so any assignment builds an unvalidated StoryText in one keystroke.
StoryDocument.ToEntity passes the body where the title goes and the title where the body goes. It compiles because of one implicit conversion operator, and it has survived since the first commit because the only code path that would notice is dead.
Trill's Stories service is four projects with a dependency-free domain at the bottom. Its Users service is two projects with everything in one assembly - same author, same week, same estate, opposite answers to what a layer is.
Pacco models aggregates-per-document honestly across ten Mongo databases, and then creates exactly one index in the whole estate - fire-and-forget, in a scope that has already been disposed - while natural-key scans sit on the message hot path.
Availability guards every save with a version-filtered ReplaceOneAsync - and discards the result, so a lost update is indistinguishable from a successful one and the integration event ships either way. Three folders away, Orders and Customers inherit the same AggregateRoot name with the versioning quietly deleted.
A single product identity is represented four ways across four DShop services - owner, replica, cart snapshot, order snapshot - each on its own refresh discipline, and nothing masters the set. Part 12 of Nine Services and a Message Bus.
Storage subscribes to almost every event in DShop, serves read-only queries, and owns nothing it cannot rebuild - the read side of a CQRS split that runs across a service boundary instead of inside one. Part 10 of Nine Services and a Message Bus.
DShop is one e-commerce shop stamped out as nine microservices over RabbitMQ - this series reads the real source to see how the topology holds together and where it drifts. Part 1 of Nine Services and a Message Bus.
The parent series covered the outbox pattern; it never compared the implementations. Entity Framework gets a real transaction, Mongo gets sessions you can switch off, expiry is three different mechanisms — and the processor that drains them all is a fire-and-forget timer.
What you get when a chassis reduces MongoDB to one builder call and a named collection - a generic repository, paging, camelCase conventions, a seeding hook - and the transactions and indexes it deliberately leaves on your desk.
Save the order, publish the event, crash in between - the dual-write problem, and how Convey's outbox and inbox turn at-least-once messaging into something you can build a business on.
Chronicle ships two persistence integrations, and reading them against the contract is a masterclass in provider sharp edges: a Redis key built from Type.GetHashCode() that changes on every restart, a Mongo delete-then-insert, and messages that may not survive the round trip.