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:08] 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
  
-<code> internal/ ├── burton/ # Bounded context for "burton" domain │ ├── 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) </code>+Application / Usecase Layer 
 +Orchestration of business logic
  
-==== 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) ===== 
  
-<code> Client (gRPC Request) ↓ 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 </code>+===== IV. Layer Responsibilities ===== 
 + 
 +^ 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 | 
 + 
 +===== V. Processing 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 ===== 
 + 
 +==== 1. Domain Layer — Define Interfaces ==== 
 + 
 +Purpose 
 + 
 +Define business contracts 
 + 
 +No implementation
  
-===== IV. Detailed Examples from Source Code =====+No framework or DB dependency
  
-==== 1. Domain LayerDefine Interfaces ====+File location: 
 +<code text> 
 +internal/domain/repository/add_point_repository.go
  
-<code go> // internal/burton/domain/repository/add_point_repository.go type IAddPointRepository interface { FetchProcessingOrders( ctx context.Context, limit, offset int, ) ([]ProcessingOrders, error)+<code go> type AddPointRepository interface { FetchProcessingOrders( ctx context.Context, limit, offset int, ) ([]ProcessingOrder, error)
 SaveOrders( SaveOrders(
     ctx context.Context,     ctx context.Context,
Line 57: Line 129:
 </code> </code>
  
-Purpose:+✔ No SQL 
 +✔ No gRPC 
 +✔ No infrastructure dependency
  
-Define business contracts+==== 2. Application Layer — Usecase Orchestration ====
  
-No implementation+Purpose
  
-No dependency on frameworks or DB+Coordinate business steps
  
-==== 2. Application / Usecase Layer: Orchestration Logic ====+Use domain interfaces only
  
-<code go// internal/burton/usecase/point_add_usecase.go type PointAddUsecase struct { repo IAddPointRepository }+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) PointAdd(ctx context.Context) error {+func (u *PointAddUsecase) Execute(ctx context.Context) error {
 orders, err := u.repo.FetchProcessingOrders(ctx, 200, 0) orders, err := u.repo.FetchProcessingOrders(ctx, 200, 0)
 if err != nil { if err != nil {
Line 75: Line 158:
 } }
  
-// Business rules+// Business calculation
-// - Calculate points: +// product_total - adjusted_merchandize_total_tax
-//   product_total - adjusted_merchandize_total_tax +
-// - Generate CSV +
-// - Upload to Yappli+
  
 return nil return nil
Line 87: Line 167:
 </code> </code>
  
-Purpose:+✔ No DB logic 
 +✔ No framework dependency 
 +✔ Mockable for unit tests
  
-Orchestrate business logic+==== 3. Interface Layer — gRPC Handler ====
  
-Depends only on Domain interfaces+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 104: 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 116: Line 203:
 </code> </code>
  
-Purpose:+✔ Thin layer 
 +✔ No business logic 
 +✔ No DB access
  
-Receive and validate requests+==== 4. Infrastructure Layer — Repository Implementation ====
  
-Delegate logic to Usecase+Purpose
  
-No business rules here+Implement domain interfaces
  
-==== 4. Infrastructure Layer: Repository Implementation ====+Handle SQL and external APIs
  
-<code go> // internal/burton/infrastructure/repository/pointadd_repository.go type AddPointRepository struct { db *sqlx.DB }+Replaceable without affecting core logic
  
-func (r *AddPointRepository) FetchProcessingOrders(+File location: 
 +<code> 
 +internal/infrastructure/repository/add_point_repository.go 
 +</code> 
 + 
 +<code go> type AddPointRepositoryImpl struct { db *sqlx.DB } 
 + 
 +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 145: Line 242:
 </code> </code>
  
-Purpose:+✔ Depends only on Domain 
 +✔ Infrastructure-specific logic only
  
-Implement domain interfaces+===== VII. Best Practices =====
  
-Access database or external services+==== 1. Dependency Injection ====
  
-Infrastructure depends on Domain, not vice versa+Always inject interfaces, not concrete implementations.
  
-===== V. Best Practices When Coding =====+<code go> usecase :PointAddUsecase{ repo: addPointRepositoryImpl, } </code>
  
-==== 1Dependencies Point Inward ====+==== 2Testing Strategy ====
  
-Domain → depends on nothing+Unit Tests
  
-Application → depends only on Domain+Test usecases with mocked repositories
  
-Presentation → depends only on Application+No DB required
  
-Infrastructure → depends on Domain (via interfaces)+Integration Tests
  
-==== 2. Use Interfaces and Dependency Injection ====+Real database
  
-Inject interfaces, not concrete structs+Test infrastructure layer only
  
-Example:+==== 3. Error Handling ====
  
-<code go>+Use structured errors
  
-mockRepo := &MockAddPointRepository{} +Log errors in:
-</code>+
  
-Enables easy unit testing and mocking+Interface layer
  
-==== 3. Separate Concerns ====+Application layer
  
-Constants → internal/constant/+==== 4. What NOT to Do ====
  
-Validation → internal/validator/+❌ Business logic in handlers
  
-Database & Transactions → internal/infrastructure/database/+❌ SQL in usecases
  
-==== 4. Testing Strategy ====+❌ Infrastructure importing application layer
  
-Unit Tests +❌ Circular dependencies
- +
-Test each layer independently +
- +
-Mock dependencies +
- +
-Integration Tests +
- +
-Use real DB and services +
- +
-==== 5. Error Handling ==== +
- +
-Use fastmedia/errors for structured errors +
- +
-Log errors using slog in:+
  
-Presentation layer +===== VIII. Example: Adding a New Feature =====
- +
-Usecase layer +
- +
-===== VI. Example: Implementing a New Feature =====+
  
 Feature: Register Customer Feature: Register Customer
  
-Steps:+Steps
  
-Domain+Domain: create CustomerRepository interface
  
-Create ICustomerRepository+Application: create RegisterCustomerUsecase
  
-Application+Interface: add gRPC handler
  
-Create RegisterCustomerUsecase+Infrastructure: implement repository using DB
  
-Presentation+Flow
  
-Create gRPC handler RegisterHandler +<code> Handler → Usecase → Repository Interface → Repository Implementation → Database </code>
- +
-Infrastructure +
- +
-Implement CustomerRepository using sqlx +
- +
-Flow: +
-<code> +
-Handler → Usecase → Repository Interface → Repository Implementation → DB +
-</code>+
  
-===== VII. Conclusion =====+===== IX. Conclusion =====
  
-By applying Clean Architecture in this project:+By following Clean Architecture:
  
 Dependencies always point inward Dependencies always point inward
  
-Interfaces decouple business logic from infrastructure+Layers are clearly separated
  
-Each bounded context (e.g. burton) is clearly separated+Business logic is framework-independent
  
-Code becomes easier to:+Code is easier to:
  
 Maintain Maintain
go/architecture/clean_arch.1769735285.txt.gz · Last modified: by phong2018