# Architecture Guide The codebase uses Clean Architecture with dependency injection to maintain separation of concerns and testability. ## Clean Architecture Layers The project is organized into four distinct layers: **1. Domain Layer** - Business entities and rules (innermost) **2. Application Layer** - Use cases and business logic **3. Infrastructure Layer** - External services and data access **4. API Layer** - HTTP handlers and routes (outermost) ### Dependency Flow Dependencies point **inward only**: ``` API → Application → Domain ← Infrastructure ``` - Domain layer has zero external dependencies - Infrastructure implements domain interfaces - Outer layers depend on inner layers, never the reverse ## Layer Responsibilities ### Domain Layer (`src/app/{module}/domain/`) The core business logic layer. **Contains:** - Entities with business rules - Repository interfaces (contracts) - Domain errors - Validation logic **Key principle**: No external dependencies. Pure business logic only. ### Application Layer (`src/app/{module}/app/`) Orchestrates domain operations to implement use cases. **Contains:** - Service interfaces and implementations - Request/response types - Transaction boundaries - Business workflow coordination **Key principle**: Uses domain interfaces, never infrastructure directly. ### Infrastructure Layer (`src/app/{module}/infra/`) Implements domain interfaces using concrete technologies. **Contains:** - Repository implementations - Database adapters - External service clients - Type conversions (domain ↔ database) **Key principle**: Depends on domain interfaces. Hidden behind abstractions. ### API Layer (`src/api/{module}/`) Handles HTTP concerns. **Contains:** - HTTP handlers - Route definitions - Request validation - Response formatting **Key principle**: Thin layer that delegates to application services. ## Dependency Injection Uses [uber-go/dig](https://github.com/uber-go/dig) for automatic dependency injection. ### Core Pattern ```go // 1. Define interface in domain type ResourceRepository interface { GetByID(ctx context.Context, id int32) (*Resource, error) } // 2. Implement in infrastructure type resourceRepository struct { store adapters.ResourceStore } // 3. Register in DI container container.Provide(func(store adapters.ResourceStore) domain.ResourceRepository { return NewResourceRepository(store) }) // 4. Inject into services container.Provide(func(repo domain.ResourceRepository) services.ResourceService { return services.NewResourceService(repo) }) ``` ### Benefits - Automatic dependency resolution - Easy testing with mocks - Clear dependency graph - No manual wiring ## Module Pattern Each business module follows a standard structure: ``` src/app/{module}/ ├── domain/ # Entities, interfaces ├── app/services/ # Business logic ├── infra/ # Implementations ├── cmd/init.go # Initialization └── module.go # DI registration ``` ### Module Registration Every module has a `module.go` file that registers its dependencies: - Repositories (infrastructure → domain interface) - Services (application layer) - Event listeners (if applicable) ### Initialization Order Defined in `src/main/cmd/init_mods.go`: 1. **Infrastructure** - Database, logging, server 2. **Shared Services** - File storage, event bus, payments 3. **Authentication** - Redis, Stytch, auth middleware 4. **Domain Modules** - Organizations, billing, etc. 5. **API Layer** - Route registration **Why order matters**: Each phase depends on previous phases being initialized. ## Resolver Pattern Bridges authentication with domain modules without creating circular dependencies. ### Problem Auth middleware needs to convert provider IDs (Stytch) to database IDs, but can't depend on domain modules directly. ### Solution Define minimal interfaces in auth package: ```go // Auth defines what it needs type OrganizationResolver interface { ResolveByProviderID(ctx context.Context, providerID string) (int32, error) } ``` Domain modules implement via adapters: ```go // Module provides implementation type orgResolverAdapter struct { repo domain.ResourceRepository } ``` Wired together in `init_mods.go` during initialization. ## Best Practices ### Constructor Pattern Always return interfaces, not concrete types: ```go // ✅ Good func NewResourceService(repo domain.ResourceRepository) services.ResourceService { return &resourceService{repo: repo} } // ❌ Bad func NewResourceService(repo domain.ResourceRepository) *resourceService { return &resourceService{repo: repo} } ``` ### Context Handling Context is always the first parameter: ```go func (s *service) CreateResource(ctx context.Context, req *Request) error ``` ### Error Wrapping Add context to errors before returning: ```go if err := s.repo.Create(ctx, resource); err != nil { return fmt.Errorf("failed to create resource: %w", err) } ``` ### Explicit Dependencies All dependencies through constructor parameters: ```go func NewResourceService( repo domain.ResourceRepository, eventBus eventbus.EventBus, logger logger.Logger, ) services.ResourceService ``` Never use global variables or hidden dependencies. ## File Locations | Pattern | File | |---------|------| | DI container setup | `src/main/cmd/root.go` | | Module initialization order | `src/main/cmd/init_mods.go` | | Module DI registration | `src/app/*/module.go` | | Package initialization | `src/pkg/*/cmd/init.go` | | API route setup | `src/api/provider.go` | ## Next Steps - **Database operations**: See [Database Guide](./database.md) - **Authentication setup**: See [Authentication Guide](./authentication.md) - **Building APIs**: See [API Development Guide](./api-development.md)