- TypeScript 88.4%
- JavaScript 9.1%
- Shell 2.3%
- Makefile 0.1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
CI / Core Gates (build · typecheck · test · lint · tasks) (push) Successful in 3m49s
CI / Integration Gates (postgres · kurrentdb · runner) (push) Successful in 2m33s
CI / Scaffold Smoke (template · scaffold · install · build · test) (push) Successful in 4m25s
Scaffold Integration Matrix / Scaffold Leg a (template · scaffold · install · build) (push) Successful in 3m18s
Scaffold Integration Matrix / Scaffold Leg b (template · scaffold · install · build) (push) Successful in 2m59s
Scaffold Integration Matrix / Scaffold Leg c (template · scaffold · install · build) (push) Successful in 3m22s
Scaffold Integration Matrix / Scaffold Leg d (template · scaffold · install · build) (push) Successful in 3m21s
|
||
| .claude | ||
| .github | ||
| .husky | ||
| .omp | ||
| .playwright-mcp | ||
| .serena | ||
| .tasks | ||
| .vscode | ||
| apps | ||
| docker | ||
| docs | ||
| packages | ||
| scripts | ||
| tools | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| .mcp.json | ||
| .prettierignore | ||
| .prettierrc | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| commitlint.config.js | ||
| CONTRIBUTING.md | ||
| docker-compose.override.yml.dist | ||
| docker-compose.yml | ||
| docker.sh | ||
| Dockerfile | ||
| eslint.config.mjs | ||
| LICENSE | ||
| Makefile | ||
| nx.json | ||
| opencode.json | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| renovate.json | ||
| sgconfig.yml | ||
| tsconfig.base.json | ||
| tsconfig.json | ||
| tsconfig.tsmorph.json | ||
| tsconfig.tsmorph.with-tests.json | ||
Effect CQRS + Event Sourcing Template
A production-ready TypeScript template implementing CQRS (Command Query Responsibility Segregation) and Event Sourcing patterns using Domain-Driven Design (DDD) and Hexagonal Architecture. Built with Effect and Nx.
🎯 What is This?
This is a template project demonstrating:
- ✅ DDD - Domain-Driven Design with bounded contexts
- ✅ CQRS - Separate write (commands) and read (queries) models
- ✅ Event Sourcing - Complete audit trail of state changes
- ✅ Hexagonal Architecture - Pluggable adapters for infrastructure
- ✅ Clean Architecture - Testable, maintainable, technology-agnostic
- ✅ Type Safety - Full TypeScript with Effect for typed error handling
Perfect for building scalable, maintainable event-driven applications.
📦 Scaffold a New Project
Generate your own copy of this architecture (pick the adapters you need; the rest are pruned) with the scaffolder:
npm create node-ddd-app <target-directory> [options]
# or: npx create-node-ddd-app <target-directory> [options]
Options:
--yes Skip prompts and scaffold the default all-memory profile (no external services needed)
--force Allow scaffolding into a non-empty directory
--no-sketches Hard-delete unselected adapters instead of keeping them in sketches/
--llm-harness LLM workflow harness tier (full|light|none)
--mcp Wire the ts-morph-readonly MCP (tsconfig.tsmorph.json + .mcp.json)
--help Print this usage and exit
See packages/create-node-ddd-app/README.md for the full guide.
🚀 Quick Start
Fastest Option: Docker (Recommended)
# Clone the repository
git clone <repo-url>
cd create-node-ddd-app
# Start with in-memory adapters (fastest)
./docker.sh dev
# Or start with PostgreSQL (event store + projections)
./docker.sh postgres
# View all available commands
./docker.sh
The API will be available at:
- Dev mode: http://localhost:3000
- PostgreSQL: http://localhost:3002
Visit /docs for interactive Swagger API documentation.
Local Development
# Install dependencies
pnpm install
# Build the application
pnpm nx run runner:build
# Run the application
node apps/runner/dist/index.js
Expected output (abridged):
🔧 Application Configuration:
├─ Environment: development
├─ Event Store: memory
├─ Event Bus: memory
├─ Auth Provider: casbin
├─ Telemetry: noop
├─ Projections:
│ ├─ ModerationQueue: memory
│ ├─ PostListView: memory
│ ├─ PostPublishStatus: memory
│ └─ ReopenedCases: memory
└─ Servers:
├─ HTTP REST: enabled (port 3000)
└─ Web SSR: enabled (port 3001)
✅ Runner: Application started successfully
🚀 CQRS + Event Sourcing architecture active
🌐 REST API: http://localhost:3000
📚 Swagger Docs: http://localhost:3000/docs
With zero configuration, the runner seeds a canonical in-memory PostListView projection plus the three moderation read models (ModerationQueue, PostPublishStatus, ReopenedCases); set PROJECTION_DEFAULT=none to opt out of seeding.
📋 Table of Contents
Core Documentation
- docs/SETUP.md - Complete setup and development guide
- docs/ARCHITECTURE.md - System design and patterns
- docs/TESTING.md - Testing strategy and execution
- docs/SECURITY.md - Security guidelines
- docs/TELEMETRY.md - Observability with SigNoz
Product & Planning
- docs/product/VISION.md - Product vision and goals
- docs/product/ROADMAP.md - Development roadmap
- docs/product/user-stories/ - Feature requirements
Architecture Decisions
- docs/decisions/ - Architecture Decision Records (ADRs)
- docs/rfcs/ - Request for Comments
For LLM Agents
- AGENTS.md - binding rules and workflow for every coding agent
- CLAUDE.md - architecture, navigation and conventions (Claude Code imports AGENTS.md from it)
🔄 CQRS + Event Sourcing Flow
┌─────────────┐
│ Client │
└──────┬──────┘
│
▼
┌─────────────────────────────────────┐
│ REST API (port 3000) │
│ Commands │ Queries │
└────────┬────────┴────────┬───────────┘
│ │
▼ ▼
┌──────────────────┐ ┌──────────────┐
│ Write Model │ │ Read Model │
│ (PostService) │ │(QueryDB) │
│ ↓ │ │ ↑ │
│ EventStore ─────┼──┼─→ Projector │
└──────────────────┘ └──────────────┘
│ ▲
└───────────────┬─┘
EventBus
(Effect Queue)
💾 API Examples
Only the GET /posts routes are public. Write routes need a bearer token, and so do the moderation reads (GET /moderation/queue, GET /moderation/cases/:id), which need case:read. The in-memory auth adapter accepts the dev tokens alice-token (admin), bob-token (editor), charlie-token (viewer) and dave-token (moderator; needed for case:read and case:resolve, which admin also holds).
Create a Post (Command)
curl -X POST http://localhost:3000/posts \
-H "Authorization: Bearer alice-token" \
-H "Content-Type: application/json" \
-d '{
"aggregateId": "post-001",
"userId": "user-123",
"title": "My First Post",
"content": "Testing CQRS and Event Sourcing!"
}'
Get a Post (Query)
curl http://localhost:3000/posts/post-001
List Posts (Query)
Returns the first 50 posts, newest first (GET /posts takes no paging parameters).
curl http://localhost:3000/posts
Publish a Post (Command)
curl -X POST http://localhost:3000/posts/post-001/publish \
-H "Authorization: Bearer alice-token"
🏗️ Architecture at a Glance
packages/
├── domain/
│ ├── shared-kernel/ # Core domain primitives (zero dependencies)
│ ├── debate-domain/ # Post aggregate, events, commands, queries
│ ├── moderation-domain/ # ModerationCase aggregate (second bounded context)
│ └── auth-domain/ # Authentication models
├── application/
│ ├── app/ # Cross-cutting ports, API definitions, error mapping
│ ├── debate-application/ # PostService, PostProjector, contract tests
│ ├── moderation-application/ # ModerationService, policies, DebateGateway
│ └── auth-application/ # AuthService, AuthorizationService
└── infrastructure/
├── eventstore-inmemory-adapter/ # Event persistence (dev/test)
├── eventstore-postgres-adapter/ # Event persistence (production)
├── eventstore-kurrentdb-adapter/ # Event persistence (KurrentDB / EventStoreDB)
├── database-inmemory-adapter/ # CQRS read model (dev/test)
├── database-postgres-adapter/ # CQRS read model (production)
├── event-queue-adapter/ # Event bus (Effect Queue)
├── http-rest-adapter/ # REST API + Swagger
└── ... # auth, telemetry, web-ssr
apps/
├── runner/ # Composition root (wires everything)
└── runner-e2e/ # End-to-end tests against the running app
packages/create-node-ddd-app/ # The scaffolder (npm create node-ddd-app)
Key Principle: Domain is stable. Infrastructure is replaceable. Runner is sacrificial.
🔧 Configuration
The application is configured via environment variables:
# Event persistence (write model)
EVENT_STORE=memory # memory, postgres, eventstoredb
# Async event distribution
EVENT_BUS=memory # memory (the only event bus)
# Read model projections (one block per projection)
# Zero-config boots a canonical in-memory PostListView projection.
PROJECTION_DEFAULT=memory # memory, postgres, none (opt out of seeding)
PROJECTION_POST_LIST_VIEW_TYPE=memory # memory, postgres
# For postgres: PROJECTION_POST_LIST_VIEW_HOST / _PORT / _DATABASE / _USER / _PASSWORD
# Server configuration
HTTP_ENABLED=true
HTTP_PORT=3000
# Telemetry
TELEMETRY_PROVIDER=noop # noop, signoz
See SETUP.md for detailed configuration guide.
🧪 Testing
# Run all tests
pnpm nx run-many -t test
# Run tests for a specific package
pnpm nx run @spr/debate-application:test
# Run tests with coverage
pnpm --filter @spr/shared-kernel run test:coverage
# End-to-end tests
pnpm test:e2e
See TESTING.md for the testing strategy (contract tests, integration tests, coverage expectations per package).
📦 Build and Deploy
Build
pnpm nx run-many -t build
Docker
# Development
./docker.sh dev
# Production setup with PostgreSQL
./docker.sh postgres
# Infrastructure only (for local Node.js debugging)
./docker.sh infra postgres signoz
See SETUP.md for complete Docker documentation.
🎓 Learning Resources
For Architects
Start with ARCHITECTURE.md to understand:
- System design philosophy
- Design patterns used
- Dependency management
- Why each architectural decision was made
For Developers
Start with SETUP.md to:
- Get your environment running
- Understand the development workflow
- Learn Docker setup and commands
- Configure environment variables
For QA Engineers
See TESTING.md for:
- How to run tests
- Testing strategies
- API endpoint testing examples
- CQRS flow verification
🛠️ Common Tasks
Run the app locally with debugger
# Terminal 1: Start infrastructure
./docker.sh infra postgres
# Terminal 2: Generate environment config (refuses to overwrite an existing
# .env.local; pass --force to replace it)
./docker.sh env:local
# Terminal 3: Build and run with inspector
pnpm nx run runner:build
node --inspect apps/runner/dist/index.js
# Connect debugger in Chrome: chrome://inspect
Test multiple adapters
# In-memory (no infrastructure needed)
pnpm nx run-many -t test
# PostgreSQL setup (runner integration tests)
./docker.sh infra postgres
pnpm --filter @spr/runner run test:integration
# SigNoz observability
./docker.sh infra postgres signoz
# View traces at http://localhost:8080
Add a new feature
- Define domain logic in
packages/domain/ - Define application services and ports in
packages/application/ - Implement adapter in
packages/infrastructure/if needed - Wire in
apps/runner/src/lib/factories.ts
🐛 Troubleshooting
Port Already in Use
HTTP_PORT=4000 node apps/runner/dist/index.js
"Adapter Not Implemented" Error
Use in-memory adapters:
EVENT_STORE=memory EVENT_BUS=memory PROJECTION_DEFAULT=memory node apps/runner/dist/index.js
Docker Issues
# Clean up everything
./docker.sh clean
# Check service health
./docker.sh health
# View logs
./docker.sh logs
📚 Additional Documentation
All documentation is organized under the docs/ folder:
| Category | Description |
|---|---|
| docs/ARCHITECTURE.md | System design, patterns, principles |
| docs/TESTING.md | Testing strategy and execution |
| docs/SETUP.md | Development setup and environment |
| docs/SECURITY.md | Security guidelines |
| docs/TELEMETRY.md | Observability with SigNoz |
| docs/decisions/ | Architecture Decision Records |
| docs/rfcs/ | Request for Comments |
| docs/product/ | Vision, roadmap, user stories |
| docs/templates/ | Document templates (RFC, ADR, LOG) |
| docs/logs/ | Operational logs |
| docs/archive/ | Historical documentation |
| docker/README.md | Docker reference |
🤝 Contributing
Contributions welcome! Please ensure:
- Architecture principles are maintained
- Tests pass and coverage doesn't decrease
- Documentation is updated
- Commits follow the project style
📄 License
MIT © Riccardo Agnoletto — see ADR-2026-07-06-repository-license.
Projects generated with create-node-ddd-app are yours: no attribution or license obligation attaches to scaffolded output.