238 lines
5.7 KiB
Markdown
238 lines
5.7 KiB
Markdown
# 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)
|