Skip to content

felipprodrigues/domain-driven-design

Repository files navigation

Domain-Driven Design: Rocket Medic Healthcare System

A practical implementation of Domain-Driven Design (DDD) principles using JavaScript/Node.js, demonstrating tactical patterns through a healthcare management system.

🎯 Overview

Rocket Medic is an educational project showcasing how to implement Domain-Driven Design patterns in a real-world healthcare domain. This implementation follows modern software architecture principles with clean separation of concerns.

πŸš€ Quick Start

# Install dependencies
npm install

# Run all tests
npm test

# Run tests with coverage
npm run coverage

# Start the API server
npm start

# Development mode (with auto-reload)
npm run dev

# Visit health check
curl http://localhost:3000/health

πŸ“š Documentation

Getting Started

DDD Concepts

Architecture Patterns

Implementation Details

πŸ—οΈ Architecture

The project follows Hexagonal Architecture (Ports & Adapters) with clear separation:

src/
β”œβ”€β”€ domain/              # Core business logic (no dependencies)
β”‚   β”œβ”€β”€ entities/       # Domain entities (Patient, Doctor, Appointment)
β”‚   β”‚   └── record/    # Medical record entities
β”‚   β”œβ”€β”€ value-objects/  # Immutable value objects (Address, WorkingHours)
β”‚   β”œβ”€β”€ services/       # Domain services (business rules)
β”‚   β”‚   └── doctor-service/ # Doctor-specific services
β”‚   └── repositories/   # Repository interfaces
β”œβ”€β”€ application/        # Use cases and application services
β”‚   └── services/      # Application services
β”œβ”€β”€ infrastructure/     # External concerns (DB, notifications)
β”‚   β”œβ”€β”€ persistance/   # Repository implementations
β”‚   └── notification/  # Notification services
β”œβ”€β”€ interfaces/        # Entry points (REST API, controllers)
β”‚   β”œβ”€β”€ controllers/   # HTTP request handlers
β”‚   β”‚   └── doctor-controllers/ # Doctor-specific controllers
β”‚   β”œβ”€β”€ routes/        # API route definitions
β”‚   └── main.js        # Application bootstrap
└── testCase/          # Manual test examples

tests/
β”œβ”€β”€ unit/              # Unit tests
β”‚   β”œβ”€β”€ domain/       # Domain layer tests
β”‚   β”‚   β”œβ”€β”€ entities/
β”‚   β”‚   β”œβ”€β”€ value-objects/
β”‚   β”‚   └── services/
β”‚   └── infrastructure/ # Infrastructure tests
β”œβ”€β”€ integration/       # Integration tests
β”‚   β”œβ”€β”€ controllers/  # Controller integration tests
β”‚   β”œβ”€β”€ repositories/ # Repository integration tests
β”‚   └── workflows/    # Full workflow tests
β”œβ”€β”€ domain/            # Domain-focused tests
β”œβ”€β”€ asyncronous/       # Async pattern tests
└── TESTING_TOOLS.md   # Testing documentation

concepts/              # DDD learning resources
└── architecture/      # Architecture pattern docs

docs/                  # Project documentation

.github/
└── workflows/         # CI/CD pipelines
    └── ci.yml        # GitHub Actions workflow

πŸ₯ Domain Model

Core Aggregates

Patient Aggregate - Manages patient information and medical history

  • Patient (root entity)
  • Medical Record
  • Allergies, Diagnoses, Treatments
  • Medications
  • Appointments and Examinations

Doctor Aggregate - Manages doctor information and availability

  • Doctor (root entity)
  • Working Hours
  • Specialties (array)
  • Availability management

Appointment Aggregate - Manages appointment scheduling

  • Appointment (root entity)
  • Patient reference
  • Doctor reference
  • Status tracking

Key Features

  • βœ… Patient management with comprehensive medical records
  • βœ… Doctor scheduling and availability tracking
  • βœ… Appointment booking with validation
  • βœ… Medical examination tracking
  • βœ… Diagnosis and medication management
  • βœ… Email notifications
  • βœ… RESTful API with Express.js
  • βœ… Automated testing with 60%+ coverage
  • βœ… CI/CD with GitHub Actions

πŸ› οΈ Technology Stack

Core

  • Runtime: Node.js 20+
  • Language: JavaScript (ES6+ modules)
  • Web Framework: Express.js 4.18.2
  • Architecture: Hexagonal/Clean Architecture with DDD patterns

Testing

  • Test Runner: Mocha 11.7.5
  • Assertions: Chai 6.2.2 (BDD style)
  • Test Doubles: Sinon 21.0.1 (stubs, spies, mocks)
  • Coverage: c8 10.1.3 (HTML, LCOV, text reporters)

Code Quality

  • Linter: ESLint 8.57.0
  • Formatter: Prettier 3.1.1

CI/CD

  • GitHub Actions: Automated testing and coverage checks
  • Coverage Threshold: 50% (lines, functions, branches, statements)

πŸ“– Learning Resources

Beginners

  1. Start with Quick Start Guide
  2. Read What is Domain-Driven Design?
  3. Explore Domain Model
  4. Run the examples in src/testCase/

Intermediate

  1. Study Aggregates patterns
  2. Understand Hexagonal Architecture
  3. Review Implementation Summary
  4. Examine the domain services

Advanced

  1. Read Development Guide
  2. Study repository implementations
  3. Extend the domain with new features
  4. Implement additional bounded contexts

πŸ§ͺ Testing

Automated Tests (38 tests)

# Run all tests
npm test

# Run tests with coverage (60.68% overall)
npm run coverage

# Open HTML coverage report
open coverage/index.html

# Run specific test file
npm test -- tests/integration/workflows/patient-appointment-workflow.test.js

Test Coverage

  • Entities: Patient, Doctor, Appointment (100% covered)
  • Value Objects: Address, EmergencyContact, Allergy (100% covered)
  • Repositories: Repository pattern with Map storage
  • Services: PatientService, DoctorService (32-66% covered)
  • Controllers: PatientController integration tests
  • Workflows: Full patient appointment workflow test

Manual Testing

# Run manual test examples
cd src && node testCase/testHospital.js

# Test API endpoints
npm start &
curl http://localhost:3000/api/patients
curl http://localhost:3000/api/doctors

CI/CD

GitHub Actions automatically runs:

  • All tests on every push
  • Coverage checks (minimum 50%)
  • ESLint validation
  • Runs on Node.js 18

πŸ“ Code Quality

# Lint code
npm run lint

# Auto-fix issues
npm run lint:fix

# Format code
npm run format

# Check formatting
npm run format:check

# Run full quality check
npm run lint && npm run format:check && npm test

Quality Tools

  • ESLint: Catches common errors and enforces coding standards
  • Prettier: Ensures consistent code formatting
  • Mocha + Chai: Comprehensive test coverage
  • c8: Istanbul-based code coverage reporting

🀝 Contributing

See Development Guide for:

  • Coding standards
  • Adding new features
  • Domain modeling guidelines
  • Testing strategies

πŸ“„ License

This is an educational project for learning Domain-Driven Design principles.

πŸ”— Related Resources


Made with ❀️ for learning Domain-Driven Design

Entities have unique identity that persists over time:

  • Patient - Identified by id and identificationDocument
  • Doctor - Identified by id and rcm (medical license)
  • Appointment - Identified by id
  • Examination - Identified by id
  • MedicalRecord - Internal entity within Patient aggregate

3. Value Objects

Value Objects are defined by their attributes, not identity:

Address Value Object

Address Value Object

class Address {
  constructor(street, number, city, state, zipCode) {
    this.street = street;
    this.number = number;
    this.city = city;
    this.state = state;
    this.zipCode = zipCode;
  }

  equals(otherAddress) {
    return (
      this.street === otherAddress.street &&
      this.number === otherAddress.number &&
      this.city === otherAddress.city &&
      this.state === otherAddress.state &&
      this.zipCode === otherAddress.zipCode
    );
  }
}

EmergencyContact Value Object

class EmergencyContact {
  constructor(name, phone) {
    this.name = name;
    this.phone = phone;
  }

  equals(otherContact) {
    return this.name === otherContact.name && this.phone === otherContact.phone;
  }
}

WorkingHours Value Object

WorkingHours Value Object

class WorkingHours {
  constructor() {
    this.hours = [];
  }

  addHours(day, timeSlot) {
    this.hours.push({ day, timeSlot });
  }

  removeHours(day, timeSlot) {
    this.hours = this.hours.filter(
      (hour) => hour.day !== day || hour.timeSlot !== timeSlot
    );
  }

  listHours() {
    return this.hours;
  }
}

πŸš€ Running the Example

Prerequisites

  • Node.js 14+ (for ES6 module support)

Setup

# Install dependencies
npm install

# Run linting
npm run lint

# Auto-fix formatting issues
npm run lint:fix

# Format all files
npm run format

# Run the test example
cd rocket-medic
node testHospital.js

Example Output

Allergy Penicillin added to patient John Doe
Appointment on 2024-07-01 scheduled for patient John Doe
Patient {
  id: '1',
  identificationDocument: '123.123.123-12',
  name: 'John Doe',
  dateOfBirth: '1990-01-01',
  gender: 'Male',
  bloodType: 'O+',
  address: Address { ... },
  phoneNumber: '+1234567890',
  email: 'johndoe@example.com',
  emergencyContact: EmergencyContact { ... },
  allergies: [ Allergy { type: 'Penicillin' } ],
  appointments: [ Appointment { ... } ],
  examinations: [ ... ],
  medicalRecord: MedicalRecord { ... }
}

🎨 Code Quality

This project uses ESLint and Prettier to enforce code quality and consistent formatting:

ESLint Rules

  • Function parameters on new lines for better readability
  • Maximum line length of 80 characters
  • 2-space indentation
  • Single quotes
  • Semicolons required

Configuration Files

πŸ“– Learning Path

  1. Start with concepts:

  2. Learn tactical patterns:

  3. Explore architecture:

  4. Study the implementation:

    • Examine the rocket-medic/ folder
    • Run and modify testHospital.js
    • Experiment with adding new features

πŸ”‘ Key Takeaways

Aggregate Design

  • Patient and Doctor are separate aggregates with clear boundaries
  • MedicalRecord is an internal entity, not a separate aggregate
  • Aggregates protect business invariants through encapsulation

Value Objects

  • Address, EmergencyContact, and WorkingHours have no identity
  • Equality is based on attribute values
  • Can be freely shared across entities

Business Logic Encapsulation

  • Business rules are enforced through aggregate methods
  • Direct manipulation of internal state is prevented
  • Validation happens at the aggregate boundary

Consistency Boundaries

  • Each aggregate maintains its own consistency
  • References between aggregates should ideally use IDs (simplified in this example)
  • One transaction per aggregate

πŸ”§ Future Enhancements

To make this a production-ready system, consider:

  1. Reference by ID: Change appointments to reference Patient and Doctor by ID
  2. Domain Events: Implement events for cross-aggregate communication
  3. Repositories: Add persistence layer with repository pattern
  4. Application Services: Create application layer for use case orchestration
  5. Validation: Add comprehensive validation for all value objects
  6. Testing: Add unit and integration tests
  7. Error Handling: Implement robust error handling and domain exceptions

πŸ“š Additional Resources

  • Eric Evans - "Domain-Driven Design: Tackling Complexity in the Heart of Software"
  • Vaughn Vernon - "Implementing Domain-Driven Design"
  • Martin Fowler - Domain-Driven Design

πŸ“„ License

This is an educational project for demonstrating DDD concepts.

About

Learn Domain-Driven Design by example! Complete DDD implementation of a healthcare management system using JavaScript, covering tactical patterns (aggregates, entities, value objects), strategic patterns (bounded contexts), and hexagonal architecture with comprehensive documentation.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors