No description
Find a file
2026-02-14 22:48:06 +07:00
cmd/server feat: example of ddd with sqlc 2026-02-14 22:48:06 +07:00
internal feat: example of ddd with sqlc 2026-02-14 22:48:06 +07:00
.gitignore feat: example of ddd with sqlc 2026-02-14 22:48:06 +07:00
ddd-with-sqlc.md feat: example of ddd with sqlc 2026-02-14 22:48:06 +07:00
docker-compose.yml feat: example of ddd with sqlc 2026-02-14 22:48:06 +07:00
go.mod feat: example of ddd with sqlc 2026-02-14 22:48:06 +07:00
go.sum feat: example of ddd with sqlc 2026-02-14 22:48:06 +07:00
README.md feat: example of ddd with sqlc 2026-02-14 22:48:06 +07:00

Pragmatic DDD + sqlc + Echo API Example

Clean, pragmatic architecture example with Go, sqlc, and Echo framework.

🏗️ Architecture (Pragmatic DDD)

cmd/
  server/
    main.go                    # Bootstrap: wires dependencies only
internal/
  domain/                     # Pure business entities
    user.go                    # User entity + UserID type
  application/                # Services (business logic orchestration)
    create_user.go             # CreateUserService
    login.go                   # AuthService
  infrastructure/
    postgres/                  # Database implementation
      schema.sql
      queries.sql
      sqlc.yaml
      user_repository.go        # UserRepository (concrete type)
      sqlc/                     # Generated by sqlc
    http/                      # HTTP server implementation
      server.go                 # Echo setup & routing
      handlers.go               # Request handlers

Architecture Principles

Layer Responsibility Dependencies
Domain Business entities None (pure Go types)
Application Business logic orchestration Domain + Infrastructure
Infrastructure External concerns (DB, HTTP) Domain + sqlc + Echo
main.go Bootstrap/wiring All layers

Key Design Decisions:

  • No repository interfaces - Concrete types for direct navigation
  • No Echo in main - HTTP setup is in infrastructure layer
  • Domain is pure - No sqlc, Echo, or external types
  • Single Responsibility - Each layer has one clear purpose
  • Navigatable - Cmd+click goes directly to implementation

Why No Interfaces?

For learning/examples with one implementation, interfaces add:

  • Unnecessary indirection
  • Worse IDE navigation (jumps to interface, not implementation)
  • Extra boilerplate code

Pragmatic approach: Application services depend directly on concrete infrastructure types. Still clean separation, just without interface abstraction.

🚀 Quick Start

1. Start Database

docker-compose up -d

2. Run Server

go run cmd/server/main.go
# Server starts on http://localhost:8080

3. Test API

Create User:

curl -X POST http://localhost:8080/users \
  -H "Content-Type: application/json" \
  -d '{
    "id": "user-1",
    "name": "John Doe",
    "email": "john@example.com",
    "password": "secret123"
  }'

Login:

curl -X POST http://localhost:8080/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "john@example.com",
    "password": "secret123"
  }'

Response:

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

📁 Code Flow Example

Login Request Flow:

  1. main.go → Creates all dependencies
  2. Echo (infrastructure/http) → Receives HTTP request
  3. Handler (infrastructure/http) → Calls AuthService
  4. AuthService (application) → Calls UserRepository directly
  5. UserRepository (infrastructure/postgres) → Queries database via sqlc
  6. Response → Flows back up through layers

What's NOT in main.go:

  • No Echo setup
  • No route definitions
  • No middleware configuration
  • Only dependency wiring (bootstrap)

This makes the code:

  • Navigable - Cmd+click goes directly to implementation
  • Maintainable - Clear separation of concerns
  • Simple - No unnecessary abstractions
  • Clean - Each layer has single responsibility