Every pattern post in this series showed one pattern on its own: a copy of CornerStore with exactly one smell fixed. That’s great for learning, but it’s not how real code works. In real code, patterns meet. A facade calls a validation chain. A state change notifies observers, which send messages through a bridge.
So the last piece of the repository is the capstone: one copy of CornerStore with all 23 patterns applied, one commit at a time, with the golden master checked after every single one.
Agenda:
- The Rules
- Where the Patterns Meet
- The Whole Flow
- The Numbers
- What the Capstone Really Teaches
- Walk Through It Yourself
The Rules
The capstone followed the same discipline as the rest of the series, just 23 times in a row:
- Start from an unchanged copy of the baseline.
- Apply one pattern per commit.
- After every commit: build with zero warnings, run every test, and check the trading-day transcript still matches the baseline byte for byte.
- Finish with zero
SMELLmarkers left in the code.
The order mattered. Singleton went first, because it touches more files than anything else, and doing it later would have meant redoing other work. After that, the patterns were applied area by area: catalogue, pricing, checkout, cart, order lifecycle, back office and reporting.
Where the Patterns Meet
This is the part the individual solutions can’t show you.
The catalogue combines Prototype, Composite, Iterator and Flyweight. Products and bundles share one item interface, variants clone their parent, iterators walk the category tree and imported listings share their specifications.
Pricing combines Abstract Factory, Strategy, Decorator and Interpreter. A regional factory supplies matching tax and surcharge rules, a strategy prices the shipping method, decorators add the optional extras and an expression tree evaluates promo rules. Four patterns, one quote.
Checkout is where it gets interesting. Remember how the Facade post showed the payment switch and the courier XML travelling into the facade, still marked as smells? In the capstone, they finally get fixed in place. The facade now builds a quote, runs the Chain of Responsibility validators, charges through a Factory Method creator, creates the order with a Builder and books the courier through the Adapter. Five patterns, one front door.
The cart pairs Memento and Mediator: snapshots for undo, and a hub that keeps the widgets in sync.
The order lifecycle chains three patterns together. A State object performs a legal transition, the order service notifies its Observers, and the notification observer sends the message through a Bridge to the customer’s email or SMS channel.
The back office combines Proxy and Command. Commands run against the protected inventory interface, the caching proxy stays accurate after every stock change, and the invoker keeps the audit log.
Reporting finishes what Template Method started. That post ended with a duplicated type switch left inside both exporters, waiting for Visitor. In the capstone, the template owns the workflow and a visitor handles each product type. Each exporter is left with one job: its output format.
The Whole Flow
Here’s a checkout from cart to notification, labelled with the pattern that handles each step:
flowchart LR
Cart["Cart state<br/>Memento"] --> Mediator["CartMediator<br/>Mediator"]
Mediator --> Facade["CheckoutFacade<br/>Facade"]
Facade --> Pricing["PricingService<br/>Abstract Factory, Strategy,<br/>Decorator, Interpreter"]
Pricing --> Validation["OrderValidator<br/>Chain of Responsibility"]
Validation --> Payment["PaymentProcessorCreator<br/>Factory Method"]
Payment --> Orders["OrderService and builders<br/>Builder"]
Orders --> Inventory["Inventory proxies<br/>Proxy"]
Inventory --> Shipping["ShipFastAdapter<br/>Adapter"]
Shipping --> State["OrderState<br/>State"]
State --> Subscribers["OrderService.NotifyObservers<br/>Observer"]
Subscribers --> Messages["Order messages and IChannel<br/>Bridge"]
Orders --> Reports["ReportService"]
Reports --> Exporter["ReportExporter<br/>Template Method"]
Exporter --> Visitor["ReportTotalsVisitor<br/>Visitor"]
When a single checkout passes through that many patterns, I find it helps to remember that each box started life as a smell in the baseline.
The Numbers
| Baseline | Capstone | |
|---|---|---|
| Source files | 55 | 124 |
| Lines of C# | 2,571 | 3,835 |
SMELL markers |
62 | 0 |
| Capstone tests | n/a | 131 |
| Trading-day transcript | 417 lines | the same 417 lines |
More than twice the files and about 50% more code, for the exact same behaviour.
And one small story from the very end. The final check, “zero SMELL markers”, caught a leftover: the Memento commit had removed four S18 markers but missed the one on the Cart class itself. It was removed in the last commit. It’s a tiny thing, but it’s exactly why you write the check down instead of trusting yourself (or an AI agent) to remember.
What the Capstone Really Teaches
Here’s my honest take after building all of this: the capstone is a showcase, not a recommendation.
Look at that table again. The capstone has more than twice the files of the baseline to do exactly the same thing. For a small shop with two regions and three shipping methods, a lot of these patterns are more structure than the problem needs. If CornerStore were a real product, I wouldn’t apply all 23. I’d apply the handful whose problems were actually hurting, and leave the rest until they did.
That’s the same lesson I ended my 2023 post on: design patterns shouldn’t be applied indiscriminately. They buy flexibility with indirection, and indirection has a cost. Remember KISS.
What the capstone does prove:
- Patterns compose. Twenty-three of them can live in one codebase without fighting, as long as each one has a clear job.
- Small, verified steps make big refactors safe. Twenty-four commits, each one building, testing and matching the transcript, add up to a complete restructure with zero behaviour change.
- A golden master is a superpower. None of this would be safe without it.
So use the capstone the way you’d use a reference book: to see how patterns can fit together. Then go back to your own code and apply only what it needs.
Walk Through It Yourself
Build and test the capstone:
dotnet build capstone\Capstone.slnx -c Release
dotnet test capstone\Capstone.slnx -c Release
The best way to explore it is commit by commit. Each one applies exactly one pattern on top of the last:
| Step | Pattern | Commit |
|---|---|---|
| 0 | Copy the baseline | c4b770f |
| 1 | Singleton | d9b1204 |
| 2 | Prototype | 7750e74 |
| 3 | Composite | 015407a |
| 4 | Iterator | 057f63e |
| 5 | Flyweight | d69980f |
| 6 | Abstract Factory | ab7f994 |
| 7 | Strategy | d682f69 |
| 8 | Decorator | 7ffe9bc |
| 9 | Interpreter | f3e3a6a |
| 10 | Factory Method | d38ab38 |
| 11 | Adapter | aec0890 |
| 12 | Builder | fab936c |
| 13 | Chain of Responsibility | 253627b |
| 14 | Facade | 6203bf9 |
| 15 | Memento | 62bfa4e |
| 16 | Mediator | 511d631 |
| 17 | State | f6f4f67 |
| 18 | Observer | eb54236 |
| 19 | Bridge | cf4c35f |
| 20 | Proxy | da5d063 |
| 21 | Command | 0446f38 |
| 22 | Template Method | cead8ef |
| 23 | Visitor | 81f424a |
The capstone README maps every pattern to the files where it lives.
There’s one post left, and it’s a different kind of story: how all of this was built.
Previous: Commonly Confused Patterns · Next: Built with Microsoft HVE Core