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:11] 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
  
-<pre> 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) </pre>+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 | pointadd_repository.go | Domain (interfaces) |+
  
-Dependency Direction:+==== 2. Dependency Rule (Most Important Rule) ====
  
-<pre> Infrastructure → Domain ← Application ← Presentation </pre>+Dependencies always point inward. 
 +Outer layers depend on inner layers. 
 +Inner layers never depend on outer layers.
  
-===== III. Processing Flow (Request → Response) =====+<code> Interface → Application → Domain Infrastructure → Domain </code>
  
-<pre> 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 </pre>+===== III. Project Folder Structure =====
  
-===== IV. Detailed Examples from Source Code =====+==== Overall Structure (Clean Architecture Only) ====
  
-==== 1. Domain LayerDefine Interfaces ====+<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> 
 + 
 + 
 +===== 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 
 + 
 +No framework or DB dependency 
 + 
 +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 56: 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 DB or frameworks+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
  
-func (u *PointAddUsecase) PointAdd(ctx context.Context) error {+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) orders, err := u.repo.FetchProcessingOrders(ctx, 200, 0)
 if err != nil { if err != nil {
Line 74: 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 86: 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 LayergRPC Handler ====+Validate input 
 + 
 +Delegate logic to usecase 
 + 
 +File location: 
 +<code> 
 +internal/interface/grpc/handler/sso_handler.go 
 +</code>
  
-<code go> // internal/presentation/grpc/handler/burton/SSO.go type SSOHandler struct { SSOService usecase.ISSOUsecase }+<code go> type SSOHandler struct { usecase application.SSOUsecase }
  
 func (h *SSOHandler) Authenticate( func (h *SSOHandler) Authenticate(
Line 103: 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 115: 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 LayerRepository Implementation ====+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 146: Line 242:
 </code> </code>
  
-Purpose:+✔ Depends only on Domain 
 +✔ Infrastructure-specific logic only
  
-Implement domain interfaces+===== VII. Best Practices =====
  
-Access DB or external services+==== 1. Dependency Injection ====
  
-Infrastructure depends on Domain+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 on Domain+Test usecases with mocked repositories
  
-Presentation → depends 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 implementations+Test infrastructure layer only
  
-Enables easy mocking+==== 3. Error Handling ====
  
-<code go> mockRepo := &MockAddPointRepository{} </code>+Use structured errors
  
-==== 3. Separate Concerns ====+Log errors in:
  
-Constants → internal/constant/+Interface layer
  
-Validation → internal/validator/+Application layer
  
-Database / Transactions → internal/infrastructure/database/+==== 4. What NOT to Do ====
  
-==== 4. Testing Strategy ====+❌ Business logic in handlers
  
-Unit Tests +❌ SQL in usecases
- +
-Test each layer independently +
- +
-Mock dependencies +
- +
-Integration Tests +
- +
-Use real DB and services +
- +
-==== 5. Error Handling ==== +
- +
-Use fastmedia/errors for structured errors +
- +
-Log using slog in:+
  
-Presentation layer+❌ Infrastructure importing application layer
  
-Usecase layer+❌ Circular dependencies
  
-===== VI. Example: Implementing a New Feature =====+===== VIII. Example: Adding 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+===== IX. Conclusion =====
  
-Implement CustomerRepository using sqlx+By following Clean Architecture:
  
-Flow:+Dependencies always point inward
  
-<pre> Handler ↓ Usecase ↓ Repository Interface ↓ Repository Implementation ↓ Database </pre>+Layers are clearly separated
  
-===== VII. Conclusion =====+Business logic is framework-independent
  
-By applying Clean Architecture in this project: +Code is easier to:
- +
-Dependencies always point inward+
  
-Interfaces decouple business logic from infrastructure+Maintain
  
-Each bounded context (e.g. burton) is clearly separated+Test
  
-Code becomes easier to maintain, test, and scale+Scale
go/architecture/clean_arch.1769735493.txt.gz · Last modified: by phong2018