Skip to content

masakifukunishi/ddd-blog-api

Repository files navigation

Blog service with DDD and Layered Architecture

This is a sample project demonstrating the implementation of a blog service using Domain-Driven Design (DDD) and Layered Architecture principles.

Directory Structure

src/

  • interface/: Interface Layer

    • controllers: HTTP Request Handling
    • middlewares: Middleware Functions
    • routes: Route Definitions
  • application/: Application Layer

    • services: Application Services
    • commands: Command Objects
    • errors: Custom Error Classes
  • domain/: Domain Layer

    • models: Entities and Value Objects
    • services: Domain Services
    • repositories: Repository Interface Definitions
  • infrastructure/: Infrastructure Layer

    • prisma: Prisma Related Configurations
    • repositories: Concrete Repository Implementations

tests/

  • unit: Unit Tests
  • helpers: Test Helper Functions

Dependency Direction

interface → application → domain ← infrastructure

Technical Stack

  • Docker
  • TypeScript
  • Node.js
  • Express
  • Prisma
  • SQLite
  • Vitest
  • ESLint
  • Prettier
  • GitHub Actions (CI)

How to run

  1. docker compose up app-dev -d
  2. docker compose exec app-dev npm run prisma:migrate-dev

How to test

  1. docker compose exec app-dev npm run test
  2. docker compose exec app-dev npm run test:coverage

Example API requests

1. Create a new user

curl -X POST http://localhost:3000/api/users \
  -H "Content-Type: application/json" \
  -d '{
    "name": "John Doe",
    "email": "john@example.com"
  }'

Expected response (201 Created)

{
  "id": 1,
  "name": "John Doe",
  "email": "john@example.com"
}

2. Get user by ID

curl -X GET http://localhost:3000/api/users/1

Expected response (200 OK)

{
  "id": 1,
  "name": "John Doe",
  "email": "john@example.com"
}

3. Create a new article

curl -X POST http://localhost:3000/api/articles \
  -H "Content-Type: application/json" \
  -d '{
    "title": "My First Article",
    "content": "This is the content of my first article.",
    "userId": 1
  }'

Expected response (201 Created)

{
  "id": 1,
  "title": "My First Article",
  "content": "This is the content of my first article.",
  "userId": 1,
  "createdAt": "2024-12-24T10:00:00.000Z"
}

4. Get article by ID

curl -X GET http://localhost:3000/api/articles/1

Expected response (200 OK)

{
  "id": 1,
  "title": "My First Article",
  "content": "This is the content of my first article.",
  "userId": 1,
  "createdAt": "2024-12-24T10:00:00.000Z"
}

5. Get user's articles

curl -X GET http://localhost:3000/api/users/1/articles

Expected response (200 OK)

[
  {
    "id": 1,
    "title": "My First Article",
    "content": "This is the content of my first article.",
    "userId": 1,
    "createdAt": "2024-12-24T10:00:00.000Z"
  }
]

6. Delete article (by owner)

curl -X DELETE http://localhost:3000/api/articles/1/users/1

Expected response (204 No Content)

7. Delete user

curl -X DELETE http://localhost:3000/api/users/1

Expected response (204 No Content)

Example requests with error responses

8. Create user with duplicate email

curl -X POST http://localhost:3000/api/users \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Jane Doe",
    "email": "john@example.com"
  }'

Expected response (409 Conflict)

9. Create article with invalid user

curl -X POST http://localhost:3000/api/articles \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Invalid Article",
    "content": "This article should not be created.",
    "userId": 999
  }'

Expected response (404 Not Found)

10. Delete article by non-owner

curl -X DELETE http://localhost:3000/api/articles/1/users/2

Expected response (403 Forbidden)

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published