This repository is a sample project demonstrating the principles of Functional Domain-Driven Design (fDDD) in TypeScript. It models a simple Restaurant Reservation System to showcase how to build a robust, testable, and maintainable application by separating core logic from infrastructure.
This project is built to be an exemplary showcase of the following software design principles:
-
Functional Core, Imperative Shell: The core business logic is written as pure, side-effect-free functions, while side effects (like database interactions, logging, or API communication) are handled at the edges of the application (the "shell").
-
Immutable Data Structures: All data types used in the domain (
Reservation,TimeSlot, etc.) are treated as immutable. We don't modify state; we create new states. -
Explicit Error Handling: The domain logic never throws exceptions. Instead, it returns a
Resulttype ({ success: true, value: T }or{ success: false, error: E }), making all possible outcomes explicit and forcing the caller to handle them. -
Dependency Inversion Principle: The application and domain layers depend on abstractions (interfaces), not on concrete implementations. This allows the infrastructure (like the database) to be swapped out with minimal changes to the core application.
-
Schema-based Validation: All incoming data from the outside world (e.g., API requests) is rigorously validated against a schema using
zodbefore it enters the application layer.
The project follows a Clean Architecture-inspired structure, adapted from the skutopia-api template:
/
├── src/
│ ├── app.ts # Express app setup
│ ├── container.ts # Dependency Injection container (tsyringe)
│ ├── contracts/ # Zod schemas and DTOs
│ ├── domain/ # The pure, functional core of the application
│ ├── http/ # Express controllers, routes, and middleware
│ ├── providers/ # Concrete infrastructure (e.g., repositories)
│ ├── repositories/ # Repository interfaces (abstractions)
│ ├── services/ # Application services (use cases)
│ └── utils/ # Shared utilities, like our Result type
├── tests/ # Unit and integration tests
├── package.json
├── tsconfig.json
└── README.md
- Language: TypeScript
- Web Framework: Express.js
- Validation:
zod - Dependency Injection:
tsyringe - Testing: Jest &
ts-jest
- Node.js (v16 or later)
- npm
- Clone the repository:
git clone <repository-url>
- Navigate to the project directory:
cd fDDD - Install the dependencies:
npm install
To start the development server, run:
npm run start:devThe server will start on http://localhost:3000.
To run the unit and integration tests, run:
npm testTo create a reservation, send a POST request to /api/reservations.
Endpoint: POST /api/reservations
Request Body:
{
"restaurantId": "1055c543-5589-446c-9390-3a9c6f85a3a2",
"userId": "a7a8f8e2-c3b4-4e5f-8a9a-1b2c3d4e5f6a",
"date": "2025-12-01T19:00:00.000Z",
"partySize": 4
}Responses:
- 201 Created: If the reservation is successful.
- 400 Bad Request: If the request body is invalid (e.g., missing fields, invalid date, party size is not a positive integer) or if the reservation is in the past.
- 409 Conflict: If the reservation cannot be made due to a business rule violation (e.g., restaurant is at full capacity).
- 500 Internal Server Error: For any other unexpected errors.