kgrzybek/modular-monolith-with-ddd
Full Modular Monolith application with Domain-Driven Design approach.
Healthy across all four use cases
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓16 active contributors
- ✓MIT licensed
- ✓CI configured
Show 3 more →Show less
- ✓Tests present
- ⚠Stale — last commit 2y ago
- ⚠Concentrated ownership — top contributor handles 57% of recent commits
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/kgrzybek/modular-monolith-with-ddd)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/kgrzybek/modular-monolith-with-ddd on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: kgrzybek/modular-monolith-with-ddd
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/kgrzybek/modular-monolith-with-ddd shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across all four use cases
- 16 active contributors
- MIT licensed
- CI configured
- Tests present
- ⚠ Stale — last commit 2y ago
- ⚠ Concentrated ownership — top contributor handles 57% of recent commits
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live kgrzybek/modular-monolith-with-ddd
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/kgrzybek/modular-monolith-with-ddd.
What it runs against: a local clone of kgrzybek/modular-monolith-with-ddd — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in kgrzybek/modular-monolith-with-ddd | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 734 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of kgrzybek/modular-monolith-with-ddd. If you don't
# have one yet, run these first:
#
# git clone https://github.com/kgrzybek/modular-monolith-with-ddd.git
# cd modular-monolith-with-ddd
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of kgrzybek/modular-monolith-with-ddd and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "kgrzybek/modular-monolith-with-ddd(\\.git)?\\b" \\
&& ok "origin remote is kgrzybek/modular-monolith-with-ddd" \\
|| miss "origin remote is not kgrzybek/modular-monolith-with-ddd (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "build/Build.cs" \\
&& ok "build/Build.cs" \\
|| miss "missing critical file: build/Build.cs"
test -f "docs/architecture-decision-log" \\
&& ok "docs/architecture-decision-log" \\
|| miss "missing critical file: docs/architecture-decision-log"
test -f "docker-compose.yml" \\
&& ok "docker-compose.yml" \\
|| miss "missing critical file: docker-compose.yml"
test -f ".github/workflows/buildPipeline.yml" \\
&& ok ".github/workflows/buildPipeline.yml" \\
|| miss "missing critical file: .github/workflows/buildPipeline.yml"
test -f "build/SUTCreator.cs" \\
&& ok "build/SUTCreator.cs" \\
|| miss "missing critical file: build/SUTCreator.cs"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 734 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~704d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/kgrzybek/modular-monolith-with-ddd"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
A production-grade C# modular monolith implementing Domain-Driven Design (DDD) with CQRS, Event Sourcing, and Outbox/Inbox patterns for inter-module communication. It demonstrates how to structure a single .NET application as independent modules with clear boundaries, preventing the typical monolith pitfalls while avoiding microservices complexity. Monolithic .NET solution structured as independent DDD modules (likely under src/) with CQRS handlers, domain entities, and application services per module. Build orchestration via Nuke (build/ directory with Build.cs, Database.cs, SUTCreator.cs) automates SQL schema deployment, test setup, and integration test execution. Docker Compose (docker-compose.yml) spins up SQL Server and supporting services for local dev.
👥Who it's for
C# backend engineers and architects designing large .NET systems who need a working reference for DDD principles, module isolation strategies, and event-driven architecture without splitting into distributed microservices. Teams evaluating whether a modular monolith fits their scalability needs.
🌱Maturity & risk
Actively maintained and production-ready. The repo has comprehensive CI/CD via GitHub Actions (buildPipeline.yml) and Azure Pipelines, extensive test coverage (architecture unit tests, integration tests, system integration tests), detailed C4 architecture documentation, and database versioning via Flyway. Recent activity visible in build scripts and schema definitions indicates ongoing development.
Low risk for learning and reference, but single-maintainer (kgrzybek) means no guarantee of emergency support. The heavy reliance on SQL Server (TSQL comprises 108K lines) creates vendor lock-in. Event Sourcing complexity and the Outbox pattern implementation require careful deployment coordination. No visible open issues or breaking changes in provided data.
Active areas of work
Active development on build pipeline reliability (buildPipeline.yml, azure-pipelines.yml present), database schema management (build/Database.cs handling migrations), and system integration testing (build/BuildIntegrationTests.cs). The presence of comprehensive C4 documentation updates and mutation testing setup suggests ongoing architectural refinement and quality assurance improvements.
🚀Get running
git clone https://github.com/kgrzybek/modular-monolith-with-ddd.git
cd modular-monolith-with-ddd
# Windows
./build.cmd
# Linux/Mac
./build.sh
# This runs the Nuke build orchestrator which restores dependencies, builds, runs tests, and provisions SQL Server
docker-compose up
Daily commands:
./build.cmd # Windows: compiles, runs unit/integration/architecture tests, provisions SQL
./build.sh # Linux/Mac: same as above
docker-compose up # Starts SQL Server and supporting services in containers
# Application starts via the build script; check build/Build.cs for entry point configuration
🗺️Map of the codebase
build/Build.cs— NUKE build orchestration entry point defining all build targets, database setup, and integration test execution.docs/architecture-decision-log— ADRs documenting core architectural decisions (modular monolith, DDD, CQRS, event-driven communication) that justify the entire codebase structure.docker-compose.yml— Defines containerized environment with database and dependencies required to run the full application locally..github/workflows/buildPipeline.yml— CI/CD pipeline definition that runs build, tests, and integration test suite on every commit.build/SUTCreator.cs— System-Under-Test factory responsible for bootstrapping modular monolith with IoC containers per module.build/Database.cs— Database initialization and migration orchestration for all modules in the monolith.
🛠️How to make changes
Add a New Module with DDD Aggregates
- Create new module folder structure with Domain, Application, Infrastructure, and Presentation layers (
build/Build.cs) - Define Aggregate root entity and Value Objects following ADR-0011 (Create rich domain models) (
docs/catalog-of-terms/Aggregate-DDD/README.md) - Implement domain events for intra-module state changes following ADR-0012 (
docs/catalog-of-terms/Domain-Event/README.md) - Register module IoC container in SUTCreator following ADR-0016 (
build/SUTCreator.cs) - Add module database migrations to Database.cs (
build/Database.cs)
Add CQRS Read Model Projection
- Subscribe to integration events from other modules via event bus (ADR-0015: in-memory events bus) (
docs/architecture-decision-log/0015-use-in-memory-events-bus.md) - Create read-side projection handler implementing 2-layered architecture (ADR-0009) (
docs/architecture-decision-log/0009-use-2-layered-architectural-style-for-reads.md) - Define Query handler to fetch denormalized read model (CQRS pattern, ADR-0007) (
docs/architecture-decision-log/0007-use-cqrs-architectural-style.md)
Add Integration Event Between Modules
- Define integration event in source module following event-driven communication (ADR-0014) (
docs/architecture-decision-log/0014-event-driven-communication-between-modules.md) - Publish event from aggregate root using domain event and Outbox pattern (ADR-0008) (
docs/architecture-decision-log/0008-allow-return-result-after-command-processing.md) - Subscribe to integration event in target module's event handler for loose coupling (
docs/catalog-of-terms/Integration-Event/README.md)
🔧Why these technologies
- .NET/C# — ADR-0003: Rich type system, strong OOP support for DDD tactical patterns, mature framework for enterprise applications
- Modular Monolith — ADR-0002: Simpler deployment than microservices, better performance than distributed, allows independent module development with clear boundaries
- Domain-Driven Design (DDD) — ADR-0011, ADR-0012: Rich domain models with Aggregates, Value Objects, and Entities to protect business invariants and express domain logic
- CQRS — ADR-0007: Separates read and write models, enables different architectural styles per concern (Clean Architecture for writes, 2-layer for reads)
- Event-Driven Architecture — ADR-0014, ADR-0015: Loose coupling between modules via in-memory event bus, enabling async inter-module communication without direct dependencies
- NUKE Build System — Enables programmatic build orchestration, database setup, and integration test execution with full C# type safety
⚖️Trade-offs already made
-
Monolith vs Microservices (ADR-0002)
- Why: Avoids distributed systems complexity while maintaining modular boundaries
- Consequence: Single deployment unit limits independent scaling; all modules share same resources and restart cycle
-
In-Memory Event Bus (ADR-0015)
- Why: Simplicity and speed for intra-process communication without external broker
- Consequence: Events lost on application crash; requires database Outbox pattern for durability; no cross-process event replay
-
Facade between API and Modules (ADR-0006)
- Why: Isolates REST API layer from business logic, enables loose coupling
- Consequence: Additional translation layer adds latency and code duplication between DTOs and Domain Models
-
CQRS with 2-layer reads, Clean Architecture for writes (ADR-0009, ADR-0010)
- Why: Optimizes read path for performance; protects write path with layered isolation
- Consequence: Increased architectural complexity and potential read/write model consistency issues
-
Rich Domain Models with Exceptions for Invariants (ADR-0011, ADR-0013)
- Why: Enforces business rules at domain layer, prevents invalid state
- Consequence: Exception-based control flow can obscure error handling; requires careful exception catching in application layer
🚫Non-goals (don't propose these)
- Real-time or event sourcing with full temporal event store replaying (Event Sourcing documented but not mandatory implementation)
- Distributed transaction management across modules (relies on eventual consistency via events)
- Multi-tenant support
- GraphQL API layer (REST API only)
- Automated schema versioning or migration strategies
🪤Traps & gotchas
SQL Server Dependency: Local dev requires a running SQL Server instance (provided by docker-compose.yml); without it, build.cmd/build.sh will hang at SqlReadinessChecker.cs awaiting connection. Flyway Migrations: Schema changes must be versioned SQL files in the migrations directory with predictable naming (e.g., V001__InitialSchema.sql); incorrect naming or manual SQL edits bypass version tracking and break repeatability. Outbox Transaction Boundary: Publishing domain events to the Outbox must occur in the same database transaction as the command; if decoupled, events may be lost or duplicated. CQRS Handler Registration: Commands and queries must be registered in the DI container; missing registration causes runtime 'handler not found' errors in production. Event Store Append: Event Sourcing uses append-only event logs; deleting or modifying events corrupts projections; reconstruct projections from events instead.
🏗️Architecture
💡Concepts to learn
- Domain-Driven Design (DDD) — The entire monolith is structured around DDD principles (bounded contexts, aggregates, domain events); understanding DDD is essential to navigate module boundaries and prevent cross-module domain logic leakage.
- CQRS (Command Query Responsibility Segregation) — Commands mutate state (via domain events), queries read projections; the repo uses separate handlers for each, enabling different optimization and consistency strategies per module.
- Event Sourcing — The application stores immutable domain events as the source of truth, reconstructing entity state by replaying events; enables audit trails, temporal queries, and consistency across modules.
- Outbox Pattern (Transactional Outbox) — Ensures domain events published to other modules are not lost if the originating module crashes; events are written to a local outbox table in the same transaction, then a background process reliably publishes them.
- Inbox Pattern (Idempotent Message Processing) — When receiving events from other modules via the Outbox, the Inbox pattern stores received event IDs to prevent duplicate processing if a message is retried; critical for consistency in asynchronous module communication.
- Modular Monolith — The repo demonstrates how to structure a single deployment unit (monolith) with independent modules that could theoretically be extracted into microservices; balances deployability and consistency without distributed complexity.
- C4 Model — The repo uses C4 diagrams (System Context, Container, Component, Class levels) to document architecture at multiple scales; essential for understanding how modules relate to external systems and each other.
🔗Related repos
ardalis/DDD-NLayer-Architecture— Similar DDD patterns in .NET but simpler, single-layer focus; good for comparing architectural trade-offs between layered and modular approaches.kgrzybek/modular-monolith-with-ddd-fe-react— Official companion frontend for this backend; React application consuming the REST/GraphQL APIs exposed by the modular monolith modules.vkhorikov/CqrsInPractice— Vladimir Khorikov's example of CQRS in .NET; demonstrates command/query separation patterns that complement the modular monolith's request handling.EventStore/EventStoreDB— Purpose-built Event Store database; this project uses SQL Server, but EventStoreDB is the specialized alternative for Event Sourcing architectures.Netflix/conductor— Workflow orchestration platform; relevant for understanding how the Outbox pattern handles async inter-module communication at scale.
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive C4 model documentation generation workflow
The repo contains PlantUML C4 diagrams (c1_system_context.puml, c2_container.puml, c3_components.puml, c4_class.puml) but lacks a CI workflow to auto-generate PNG/SVG artifacts. This PR would add a GitHub Action to validate PlantUML syntax, generate diagram images on each commit, and attach them to releases. Currently only static PNGs exist in docs/C4/, making diagrams difficult to version-control and update.
- [ ] Create .github/workflows/plantuml-diagrams.yml that runs PlantUML CLI on docs/PlantUML/ directory
- [ ] Configure workflow to generate PNG outputs and commit them to docs/C4/ on main branch updates
- [ ] Add PlantUML validation step to catch syntax errors early
- [ ] Update README with documentation on maintaining C4 diagrams and the auto-generation process
Implement architecture decision log validation in CI/CD pipeline
The repo has a well-structured ADL (docs/architecture-decision-log/) following MADR format, but there's no validation in buildPipeline.yml to ensure new ADRs follow the template or that they're properly referenced. This PR would add linting and consistency checks to catch incomplete ADR submissions before merge.
- [ ] Add schema validation in build/Build.cs or new build/ArchitectureDecisionLog.cs to check ADL files follow MADR format (Status, Context, Decision, Consequences fields)
- [ ] Create GitHub Action or NUKE task to verify each ADL file has a numeric prefix (000X-*.md pattern)
- [ ] Add check to buildPipeline.yml that ensures ADL files are referenced in docs or README
- [ ] Document ADL contribution guidelines in CONTRIBUTING.md (currently missing)
Add Docker Compose health checks and SUT preparation integration tests
The repo has docker-compose.yml for local development and build/SUTCreator.cs for test infrastructure, but lacks integration tests validating the full Docker Compose stack startup and database readiness. The build/Utils/SqlReadinessChecker.cs is underutilized. This PR would add end-to-end validation that the SUT (System Under Test) properly initializes.
- [ ] Add HEALTHCHECK directives to docker-compose.yml services (currently missing)
- [ ] Create build/BuildIntegrationTests.cs test for validating SqlReadinessChecker works against docker-compose environment
- [ ] Add GitHub Action workflow (.github/workflows/docker-compose-validation.yml) that spins up docker-compose and verifies all services reach healthy state
- [ ] Document in README how developers can verify local SUT is ready before running tests
🌿Good first issues
- Add mutation tests for a single module's domain layer: Pick
src/[Module]/Domain/(e.g., Meetings or Payments visible in docs), run mutation testing via Stryker.NET, and document which mutation operators are most valuable for DDD entities; helps new contributors understand fault injection without complex distributed changes. - Expand C4 component diagram for module internal architecture: The C3 diagram at
docs/C4/c3_components.pumlexists but internal CQRS/Event Sourcing flow within a module (command → domain event → outbox → projection) could be illustrated as a detailed sub-diagram; improves onboarding for developers unfamiliar with the pattern. - Create a 'Add a New Module' checklist and template: Write a markdown guide with a skeleton directory structure and minimal viable Command/Handler/Entity/Event; include build.sh steps to scaffold it. Place at
docs/HOW_TO_ADD_MODULE.mdwithbuild/ModuleTemplate/directory. Reduces friction for contributors adding features to new bounded contexts.
⭐Top contributors
Click to expand
Top contributors
- @kgrzybek — 57 commits
- @AndreiGanichev — 8 commits
- @GregoireWulliamoz — 7 commits
- @bistok — 4 commits
- @jflaga — 4 commits
📝Recent commits
Click to expand
Recent commits
91c8ef2— Update GitHub Actions and .NET setup versions to 4 (#322) (bistok)6f70797— Fix connection string for Registrations (#321) (jflaga)38e49f5— Update README.md (#304) (vikramvee)0f3557a— Fix docker build (#299) (AlmarAubel)faf4240— refactor(Meetings): use simplified HttpPatch Attribute name (#301) (dushanpantic)04a91f2— build(deps): bump System.Data.SqlClient from 4.8.5 to 4.8.6 in /build (#302) (dependabot[bot])5018ca9— Resolved multiple issues (#307) (vikramvee)b2b65e9— Add missing tables for registrations (#315) (jflaga)f2d7553— fix: fix for db related stuff in "How to Run" section of README (#316) (jflaga)c9da7af— fix: missing $ in sql string GetAuthenticatedUserPermissionsQueryHandler (#319) (jflaga)
🔒Security observations
Failed to generate security analysis.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.