The Problem

This repo is a seed project for experimenting with Domain-Driven Design (DDD) and related patterns in a TypeScript/Express stack. It solves the "how do I structure a Node.js API around domain logic rather than routes" problem, demonstrating a layered architecture with dependency injection, CQRS-style commands, and TypeORM persistence.

What This Does

ts-express-ddd-seed is a small Express API with two domain entities—Customer and Movie—each with its own application service, repository interface, and TypeORM-backed implementation. The code is organized into src/domain, src/application, src/infrastructure, and src/presentation folders, following the classic DDD layering.

Dependency injection is handled by src/ioc-container.ts using Inversify. The container wires the application services to their concrete TypeORM repositories, so the controllers in src/presentation/rest/ never touch persistence directly.

How It Is Wired

Execution starts at src/app.ts, which bootstraps the Express server and loads the IoC container. Requests hit the controllers (movie.controller.ts, customer-controller.ts), which delegate to application services (movie-store-application-service.ts, customer-application-service.ts). Those services call repository interfaces, resolved to typeorm-customer-repository.ts and typeorm-movie-repository.ts by the container.

The import graph shows 21 modules with 45 edges and no circular dependencies—clean layering. The hub is src/ioc-container (1 importer, 9 imports, instability 0.9), meaning it's the most volatile wiring point. The domain entities (src/domain/customer, src/domain/movie) have zero outgoing imports, which is correct for DDD—they're the stable core.

The call graph shows a straightforward flow: registerMovie and registerCustomer both call create, which routes through _repo (called from 5 places) to getRepo in typeorm-connection-service.ts. That's the only file that touches the database—3 functions read or write. rentMovie calls findById twice, then rentTo on the domain entity.

The file map: typeorm-customer-repository.ts owns customer persistence (4 functions), typeorm-movie-repository.ts owns movie persistence (7 functions, including list, update, delete), and typeorm-connection-service.ts manages the TypeORM connection. src/domain/movie.ts holds the rentTo domain logic—the one place business rules live outside the database layer.

How To Use It

git clone https://github.com/moses-y/ts-express-ddd-seed
cd ts-express-ddd-seed
npm install
npm start

Configuration lives in config/app-config.ts and ormconfig.json (TypeORM connection settings). The README documents npm run test:e2e:coverage for tests, npm run lint for TSLint, and npm run release to run all checks. No environment variables are documented; the app expects a local database per ormconfig.json.

Real-World Use

This fits as a starting template for a small service with a few bounded contexts. If you're building a rental system, the Movie.rentTo domain method is where you'd enforce business rules (e.g., "movie must be available"), while MovieStoreApplicationService.rentMovie orchestrates the transaction. The CQRS commands (RegisterMovieCommand, RentMovieCommand) show the pattern for separating write operations from reads.

Code Health & Issues

Static analysis found 8 issues: 4 high, 4 medium. The high-severity findings are:

  • High – No LICENSE file; all rights reserved by default, which blocks reuse in client work. Add MIT or Apache-2.0.
  • High – No lockfile; builds are non-reproducible. Commit the generated lockfile.
  • High – No CI pipeline; nothing runs the build or tests on changes. Add a workflow for push and pull_request.
  • High – No build gate for the Dockerfile; the image has never been validated automatically.

Medium findings: no Dependabot/Renovate, mutable Docker base image tag, no non-root USER in the Dockerfile, and a postinstall script in package.json that fetches a binary.

The repo has 2 e2e test files and no unit tests. No secrets were found.

The Bottom Line

This is a clean, well-layered DDD seed with no circular dependencies—a solid starting point for learning the pattern or scaffolding a small service. It's not production-ready: no CI, no lockfile, no license, and the Docker setup is incomplete. Use it as a reference for structure, not as a deployable baseline.