User Tools

Site Tools


go:architecture:clean_arch

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
go:architecture:clean_arch [2026/01/30 01:15] – [1. Domain Layer: Define Interfaces] phong2018go:architecture:clean_arch [2026/01/30 01:33] (current) – [2. Application Layer — Usecase Orchestration] phong2018
Line 1: Line 1:
-====== A Guide to Using Clean Architecture with DDD (Domain-Driven Design) in Go ======+====== A Guide to Using Clean Architecture in Go ======
  
 ===== I. Introduction to Clean Architecture ===== ===== I. Introduction to Clean Architecture =====
  
-Clean Architecture is a software architecture proposed by Robert C. Martin+Clean Architecture is a software architecture proposed by Robert C. Martin (Uncle Bob).
-Its goal is to clearly separate layers so the system is easier to maintain, extend, and test.+
  
-The pascalia-spec project applies Clean Architecture with 4 main layers:+Its main goals are:
  
-Domain Layer: Core business logic (entities, interfaces, business rules)+Clear separation of concerns
  
-Application / Usecase Layer: Application logic (orchestration, use cases)+Reduced coupling between layers
  
-Presentation Layer: User interface communication (handlersAPIs)+Easier testingmaintenance, and evolution
  
-Infrastructure Layer: External technical concerns (DB, external APIs)+This project follows Clean Architecture only. 
 +No Domain-Driven Design (DDDor Bounded Context concepts are required.
  
-Key Principle:+===== II. Core Principles =====
  
-Dependencies always point inward (outer layers depend on inner layers, never the opposite).+==== 1Layered Separation ====
  
-===== II. Folder Structure and Dependencies =====+The system is divided into four logical layers:
  
-==== Overall Structure ====+Domain Layer 
 +Core business rules and contracts
  
-  internal/ +Application / Usecase Layer 
-  ├── burton/                          # Bounded context for "burton" domain +Orchestration of business logic
-  │   ├── domain/                      # Domain Layer +
-  │   │   └── repository/              # Interfaces (e.g., IAddPointRepository) +
-  │   ├── infrastructure/              # Infrastructure Layer +
-  │   │   └── repository/              # Repository implementations (DB access) +
-  │   └── usecase/                     # Application / Usecase Layer +
-  ├── presentation/                    # Presentation Layer +
-  │   └── grpc/ +
-  │       └── handler/                 # gRPC handlers +
-  ├── constant/                        # Constants (cross-cutting) +
-  ├── infrastructure/                  # Shared infrastructure (DB transactions) +
-  └── validator/                       # Input validation (cross-cutting)+
  
-==== Roles and Dependencies ====+Interface (Presentation) Layer 
 +gRPC / HTTP handlers
  
-Layer ^ Role ^ Example ^ Depends On ^ +Infrastructure Layer 
-| Domain | Core business logic | IAddPointRepository, business rules | Nothing | +Database and external service access
-| Application / Usecase | Application logic | PointAddUsecase | Domain | +
-| Presentation | User communication | SSOHandler | Application | +
-| Infrastructure | External technical concerns | pointadd_repository.go | Domain (interfaces) |+
  
-Dependency Direction: +==== 2. Dependency Rule (Most Important Rule) ==== 
-<code> + 
-Infrastructure → Domain ← Application ← Presentation+Dependencies always point inward. 
 +Outer layers depend on inner layers. 
 +Inner layers never depend on outer layers. 
 + 
 +<code> Interface → Application → Domain Infrastructure → Domain </code> 
 + 
 +===== III. Project Folder Structure ===== 
 + 
 +==== Overall Structure (Clean Architecture Only) ==== 
 + 
 +<code text> 
 +internal/ 
 +├── domain/                 # Domain Layer (pure business logic) 
 +│   ├── model/              # Core business entities 
 +│   ├── repository/         # Repository interfaces (contracts) 
 +│   └── service/            # Domain services (optional) 
 +│ 
 +├── application/            # Application Layer 
 +│   ├── usecase/            # Use cases (business orchestration) 
 +│   └── dto/                # Input / output data structures 
 +│ 
 +├── interface/              # Interface (Presentation) Layer 
 +│   ├── grpc/ 
 +│   │   └── handler/        # gRPC handlers 
 +│   └── http/               # HTTP handlers (optional) 
 +│ 
 +├── infrastructure/         # Infrastructure Layer 
 +│   ├── repository/         # Repository implementations 
 +│   ├── persistence/        # Database setup & transactions 
 +│   └── client/             # External API / service clients 
 +│ 
 +└── shared/                 # Cross-cutting concerns 
 +    ├── constant/ 
 +    ├── validator/ 
 +    ├── logger/ 
 +    └── database/
 </code> </code>
  
-===== III. Processing Flow (Request → Response) ===== 
  
-  Client (gRPC Request) +===== IV. Layer Responsibilities =====
-          ↓ +
-  Presentation Layer (SSOHandler) +
-    - Receive request +
-    - Validate input +
-          ↓ +
-  Application Layer (ISSOUsecase) +
-    - Orchestrate business logic +
-          ↓ +
-  Domain Layer (Repository Interface) +
-    - Define contracts +
-          ↓ +
-  Infrastructure Layer (Repository Implementation) +
-    - Access DB / External API +
-          ↓ +
-  Database / External Service+
  
-==== Detailed Examples from Source Code ====+^ Layer ^ Responsibility ^ Depends On ^ 
 +| Domain | Business rules and contracts | Nothing | 
 +| Application | Usecase orchestration | Domain | 
 +| Interface | Request / response handling | Application | 
 +| Infrastructure | DB and external APIs | Domain |
  
-==== 1. Domain LayerDefine Interfaces ====+===== VProcessing Flow (Request → Response) ===== 
 + 
 +<code text> 
 +Client (gRPC / HTTP Request) 
 +        ↓ 
 +Interface Layer (Handler) 
 +  - Receive request 
 +  - Validate input 
 +        ↓ 
 +Application Layer (Usecase) 
 +  - Orchestrate business logic 
 +        ↓ 
 +Domain Layer (Repository Interface) 
 +  - Define contracts 
 +        ↓ 
 +Infrastructure Layer (Implementation) 
 +  - Access DB / External API 
 +        ↓ 
 +Database / External Service 
 +</code> 
 +===== VI. Detailed Layer Examples =====
  
-  // internal/burton/domain/repository/add_point_repository.go +==== 1Domain Layer — Define Interfaces ====
-  type IAddPointRepository interface { +
-      FetchProcessingOrders( +
-          ctx context.Context, +
-          limit, offset int, +
-      ) ([]ProcessingOrders, error) +
-      SaveOrders( +
-          ctx context.Context, +
-          orders []ValidOrder, +
-      ) error +
-  }+
  
-Purpose:+Purpose
  
-Define contracts for business logic+Define business contracts
  
 No implementation No implementation
  
-No dependency on DB or frameworks+No framework or DB dependency
  
-=== 2. Application / Usecase LayerOrchestration Logic ===+File location: 
 +<code text> 
 +internal/domain/repository/add_point_repository.go
  
-<code go> // internal/burton/usecase/point_add_usecase.go type PointAddUsecase struct repo IAddPointRepository }+<code go> type AddPointRepository interface FetchProcessingOrders( ctx context.Context, limit, offset int, ) ([]ProcessingOrder, error) 
 +SaveOrders( 
 +    ctx context.Context, 
 +    orders []ValidOrder, 
 +) error
  
-func (u *PointAddUsecase) PointAdd(ctx context.Context) error { + 
-orders, err := u.repo.FetchProcessingOrders(ctx, 200, 0) // Batch size 200+
 +</code> 
 + 
 +✔ No SQL 
 +✔ No gRPC 
 +✔ No infrastructure dependency 
 + 
 +==== 2. Application Layer — Usecase Orchestration ==== 
 + 
 +Purpose 
 + 
 +Coordinate business steps 
 + 
 +Use domain interfaces only 
 + 
 +Easy to unit test 
 + 
 +File location: 
 +<code> 
 +internal/application/usecase/point_add_usecase.go 
 + 
 +</code> 
 + 
 +<code go> 
 +type PointAddUsecase struct { repo domain.AddPointRepository } 
 + 
 +func (u *PointAddUsecase) Execute(ctx context.Context) error { 
 +orders, err := u.repo.FetchProcessingOrders(ctx, 200, 0)
 if err != nil { if err != nil {
 return err return err
 } }
-// Calculate points:+ 
 +// Business calculation:
 // product_total - adjusted_merchandize_total_tax // product_total - adjusted_merchandize_total_tax
-// Generate CSV, upload to Yappli+
 return nil return nil
 +
 +
 } }
 </code> </code>
  
-Purpose:+✔ No DB logic 
 +✔ No framework dependency 
 +✔ Mockable for unit tests
  
-Orchestrate business logic+==== 3. Interface Layer — gRPC Handler ====
  
-Use interfaces, not implementations+Purpose
  
-Easy to unit test using mocks+Handle incoming requests
  
-=== 3. Presentation Layer: gRPC Handler ===+Validate input
  
-<code go// internal/presentation/grpc/handler/burton/SSO.go type SSOHandler struct { SSOService usecase.ISSOUsecase }+Delegate logic to usecase 
 + 
 +File location: 
 +<code> 
 +internal/interface/grpc/handler/sso_handler.go 
 +</code> 
 + 
 +<code go> type SSOHandler struct { usecase application.SSOUsecase }
  
 func (h *SSOHandler) Authenticate( func (h *SSOHandler) Authenticate(
Line 127: Line 193:
 ) (*pb.AuthResponse, error) { ) (*pb.AuthResponse, error) {
  
-// Validate input 
 if err := validator.ValidateSSO(req); err != nil { if err := validator.ValidateSSO(req); err != nil {
     return nil, err     return nil, err
 } }
  
-// Call usecase +return h.usecase.Authenticate(ctx, req)
-return h.SSOService.Authenticate(ctx, req)+
  
  
Line 139: Line 203:
 </code> </code>
  
-Purpose:+✔ Thin layer 
 +✔ No business logic 
 +✔ No DB access
  
-Receive requests+==== 4. Infrastructure Layer — Repository Implementation ====
  
-Validate input+Purpose
  
-Delegate logic to usecase+Implement domain interfaces
  
-No business rules here+Handle SQL and external APIs
  
-=== 4. Infrastructure LayerImplementation ===+Replaceable without affecting core logic 
 + 
 +File location: 
 +<code> 
 +internal/infrastructure/repository/add_point_repository.go 
 +</code>
  
-<code go> // internal/burton/infrastructure/repository/pointadd_repository.go type AddPointRepository struct { db *sqlx.DB }+<code go> type AddPointRepositoryImpl struct { db *sqlx.DB }
  
-func (r *AddPointRepository) FetchProcessingOrders(+func (r *AddPointRepositoryImpl) FetchProcessingOrders(
 ctx context.Context, ctx context.Context,
 limit, offset int, limit, offset int,
-) ([]ProcessingOrders, error) {+) ([]ProcessingOrder, error) {
  
 query := ` query := `
     SELECT ...     SELECT ...
-    FROM customers c +    FROM orders
-    INNER JOIN orders o ...+
     LIMIT ? OFFSET ?     LIMIT ? OFFSET ?
 ` `
-return r.db.SelectContext(ctx, &orders, query, limit, offset)+var orders []ProcessingOrder 
 +err := r.db.SelectContext(ctx, &orders, query, limit, offset) 
 +return orders, err
  
  
Line 170: Line 242:
 </code> </code>
  
-Purpose:+✔ Depends only on Domain 
 +✔ Infrastructure-specific logic only
  
-Implement domain interfaces+===== VII. Best Practices =====
  
-Handle DB / external APIs+==== 1. Dependency Injection ====
  
-Depends on Domain, not Usecase or Presentation+Always inject interfaces, not concrete implementations.
  
-===== IV. Best Practices When Coding =====+<code go> usecase :PointAddUsecase{ repo: addPointRepositoryImpl, } </code>
  
-==== 1. Dependencies Point Inward ==== +==== 2. Testing Strategy ====
- +
-Domain → depends on nothing +
- +
-Application → depends on Domain +
- +
-Presentation → depends on Application +
- +
-Infrastructure → depends on Domain (via interfaces) +
- +
-==== 2. Use Interfaces and Dependency Injection ==== +
- +
-Inject interfaces, not concrete structs +
-Example: usecase.ISSOUsecase +
- +
-Easy to mock for testing: +
-<code go> +
-mockRepo := &MockAddPointRepository{} +
-</code> +
- +
-==== 3. Separate Concerns ==== +
- +
-Constants → internal/constant/ +
-Example: DefaultChunk = 200 +
- +
-Validation → internal/validator/ +
- +
-Transactions / DB setup → internal/infrastructure/database/ +
- +
-==== 4. Testing Strategy ====+
  
 Unit Tests Unit Tests
  
-Test each layer independently+Test usecases with mocked repositories
  
-Mock dependencies+No DB required
  
 Integration Tests Integration Tests
  
-End-to-end with real DB+Real database
  
-Example:+Test infrastructure layer only
  
-Test usecase with a mock repository+==== 3. Error Handling ====
  
-==== 5. Error Handling ====+Use structured errors
  
-Use fastmedia/errors for structured errors+Log errors in:
  
-Log with slog in:+Interface layer
  
-Presentation layer+Application layer
  
-Usecase layer+==== 4. What NOT to Do ====
  
-===== V. Example: Implementing a New Feature =====+❌ Business logic in handlers
  
-Feature: Register Customer+❌ SQL in usecases
  
-Steps:+❌ Infrastructure importing application layer
  
-Domain+❌ Circular dependencies
  
-Create ICustomerRepository interface+===== VIII. Example: Adding a New Feature =====
  
-Application+Feature: Register Customer
  
-Create RegisterCustomerUsecase+Steps
  
-Presentation+Domain: create CustomerRepository interface
  
-Create gRPC handler RegisterHandler+Application: create RegisterCustomerUsecase
  
-Infrastructure+Interface: add gRPC handler
  
-Implement CustomerRepository using sqlx+Infrastructure: implement repository using DB
  
-Flow: +Flow 
-<code> + 
-Handler → Usecase → Repository Interface → Repository Implementation → DB +<code> Handler → Usecase → Repository Interface → Repository Implementation → Database </code>
-</code>+
  
-===== VI. Conclusion =====+===== IX. Conclusion =====
  
-By following Clean Architecture in this project:+By following Clean Architecture:
  
 Dependencies always point inward Dependencies always point inward
  
-Interfaces decouple layers+Layers are clearly separated
  
-Each bounded context (e.g. burton) is cleanly separated+Business logic is framework-independent
  
-Code becomes easier to:+Code is easier to:
  
 Maintain Maintain
go/architecture/clean_arch.1769735755.txt.gz · Last modified: by phong2018