The Repo That Ships Its Own Design Decisions
Most repositories ship code and a README - this one ships four ADRs with a named approver, a context map, a domain message flow and a methodology poster. Part 1 of a series that reads the strategic half of DevMentors' GroupFlights.
Open almost any repository and ask it why. Why are there eight modules and not four. Why does this one use Clean Architecture and that one a service class with an IRepository. Why does the payment integration go through a facade while the notification integration does not. The code will answer what with perfect precision and refuse to answer why at all, because the why lived in a whiteboard photograph, a Slack thread and two people's memory, and only one of those three is still employed.
GroupFlights — the teaching repository by DevMentors at github.com/devmentors/group-flights-ddd, the companion codebase to their Polish-language course Domain-Driven Design “Pragmatycznie” — answers why. It ships four architecture decision records, each with a named reporter and a named approver. It ships a context map as a checked-in PNG that an ADR references by relative path. It ships a Domain Message Flow diagram for the single hardest scenario in the domain. It ships a poster of the modelling process the team followed, credited to ddd-crew/ddd-starter-modelling-process. Fourteen and a half thousand lines of C# across thirty-five projects, and the most interesting artefacts in the whole thing are the four markdown files in architecture-decisions/.
To be clear up front: I did not write GroupFlights, and I have never worked with DevMentors. I am a source-reader. Everything in this series comes from reading the repository at commit a19b337 on master — a history of exactly three commits, of which 2473c13 Init landed all thirty-five projects at once. Where an implementation is the story I will quote it in short attributed excerpts and name the commit; everything else is fresh illustrative C# written for the article.
One thing you must know before you clone it. There is no LICENCE file. No LICENSE, no LICENSE.md, no COPYING, no SPDX header in any of the 439 .cs files, no PackageLicenseExpression in any .csproj. Public visibility on GitHub grants you the platform's terms — you may view it and fork it on GitHub — and nothing else. All rights are reserved by default. So: read it, learn from it, cite it. Do not vendor it into a project, do not lift code from it, and do not assume an open-source licence exists because the repository is public. That is not a criticism of DevMentors, who published a course companion rather than a library. It is a reading condition, and it constrains this series too.
What “strategy before code” actually means here
The README opens by listing what the repository is meant to demonstrate, and it is not a feature list. Translated from the Polish, it claims to show the result of a whole software development process based on Domain-Driven Design, including discovery of the AS-IS state, discovery of the TO-BE state, subdomain distillation, bounded-context design, bounded-context integration design, and only then tactics — implementation based on decisions already taken at the strategic level.
That ordering is the thesis of this series and it is testable. If strategy really came first, you should be able to open an ADR, read a decision, and find its fingerprints in the compiler-checked structure of the solution. Sometimes you can, and the trace is startlingly clean — part 10 scores one ADR step by step against the code and finds nine of its ten documented steps implemented. Sometimes you cannot, and the gap is the most instructive thing on the page.
The estate under test is a modular monolith: eight modules in one process, one Postgres database, seven schemas, no message broker. Eight <Module>.Api projects are registered in Program.cs:19-26 in a fixed order, and every one of them is a bounded context with its own persistence, its own vocabulary and its own application architecture.
| Module | Application architecture | Schema | HTTP endpoints |
|---|---|---|---|
| Sales | Clean Architecture, rich domain | sales |
9 |
| Postsale | Clean Architecture, encapsulated domain | postsale |
3 |
| Inquiries | CRUD with a thin model | inquiries |
5 |
| TimeManagement | CRUD, classic relational | time-management |
0 |
| Finance | CRUD | finance |
2 |
| WorkloadManagement | CRUD with a real aggregate | workloads |
2 |
| Backoffice | CRUD | backoffice |
2 |
| Communication | stub | none | 0 |
DevMentors' other modular monolith, the ModularMonolith sample, gets its own series here — One Process, Three Modules — and it is a mechanics story: how a module registers, how an in-process bus is built, how the compiler holds a border. This series deliberately does not re-derive any of that. GroupFlights is the decisions story. Where the two estates touch I will point at the sibling series in one sentence and move on.
The declared omissions are the frame
Nine bullets in the README are explicit statements of what the system does not do. It does not implement authentication or authorisation — the “naive imitation” reads X-UserId and X-CashierId headers and checks them against a declaration in the endpoint definition. It uses no external message broker; asynchrony is an in-memory dispatcher that deliberately publishes with a delay and outside the transaction. It contains only sample unit tests, present to contrast a model with public getters against a fully encapsulated one. It implements three persistence approaches on purpose, “na potrzeby dydaktyczne” — for teaching purposes. It has no email gateway, only a message written to the console. It has no payment integration, only a fake gateway that auto-pays after five seconds.
Read that list carefully and a rule falls out that governs the rest of this series. A declared omission is a design decision; an undeclared one is a bug. Six unit test methods across 14,559 lines is not under-testing when the README says the tests exist to stage a contrast — calling this repository “well tested” would be false, and calling it “under-tested” would miss the point entirely. But a cross-cutting decorator that disables an entire context relationship behind var enabled = false; is nowhere in that list, and neither is a handler that treats a missed payment deadline as a completed payment. Those get part 14 and part 13 respectively, and they get them fairly, because teaching code earns a different standard of criticism than a library you are about to put in production.
One more reading condition. The repository is bilingual by convention and consistently so: Polish for humans, English for compilers. Every type name, member, route and JSON field is English; every exception message, code comment and TODO is Polish. PaymentRegistry.cs:5 carries //TODO: Przepisac na wlasciwe persistence — “rewrite to proper persistence”. Where the wording of a Polish string carries a finding I translate it in the text and give the original in parentheses. Watch the encoding if you clone it: the ADRs keep their diacritics and the C# source has mostly lost them, so you will see nieprawidlowa and nieprawidłowym in the same estate.
Where the series goes
- The repo that ships its own design decisions — this post.
- Eight modules, three architectures, one domain — subdomain distillation, and the inversion nobody documented.
- The context map they drew — ADR 03, relationship by relationship, in its own words.
- The context map the compiler drew — deriving the real map from the project graph, then diffing.
- Three boxes that were never built — absences as findings.
- A gentleman's agreement across thirty-five projects — ADR 01 chose verbal enforcement, and it held.
- The only legal edge between modules — the
<Module>.Sharedconvention, and where it leaks. - Your contract project is not your contract — seven of thirteen events have one consumer, itself.
- The contract that became someone else's migration — a published type mapped into another module's schema.
- Splitting a bounded context, with the ADR attached — ADR 04 scored step by step.
- The snapshot with no version — the one risk the ADR names and the code does not mitigate.
- Correlation identifier, invented three times — one pattern, three implementations, one repository.
- Broadcast without a filter — two multi-subscriber channels, two missing guards.
- The feature flag that is a constant — architecture drift you can read in one line.
- Decisions with an approver and no date — documentation as a versioned artefact with its own drift.
- What strategy-first actually bought them — the honest retrospective.
If you have ever inherited a system where the modules are obviously deliberate and nobody can tell you what the deliberation was, the next fifteen parts are a worked example of the alternative — including the parts where writing the decision down was not enough. We start with the decision that shaped everything else: which modules exist, and why they are shaped so differently from one another.