This document defines the standard pattern for building new modules in AthenAI. Following this pattern ensures modularity, testability, and maintainability across the codebase.
Each module must have the following structure:
internal/<module_name>/
├── dto/ # Data Transfer Objects (Creation, Update, Response)
├── handler/ # HTTP layer - converts service responses to standardized HTTP responses
├── interfaces/ # Repository, Service, Handler contracts
├── module/ # Dependency injection: repo → service → handler → router
├── repository/ # Data layer - handles database operations, returns raw SQL errors
├── router/ # Chi router setup
└── service/ # Business logic - transforms data, maps SQL errors to APIError
Each module must wire dependencies in this order:
- Repository: Handles DB operations, returns raw SQL errors.
- Service: Contains business logic, maps SQL errors to APIError.
- Handler: HTTP layer, converts service responses to HTTP responses.
- Router: Defines RESTful endpoints and maps them to handler methods.
- Module: Wires everything together and exposes a
New<ModuleName>Module(db *sql.DB) http.Handlerfunction.
func NewExampleModule(db *sql.DB) http.Handler {
repo := repository.NewExampleRepository(db)
service := service.NewExampleService(repo)
handler := handler.NewExampleHandler(service)
return router.NewExampleRouter(handler)
}- Interfaces: Define interfaces for repository, service, and handler in
interfaces/. - DTOs: Use DTOs for all data transfer between layers.
- Error Handling: Repository returns raw SQL errors; service maps to APIError; handler converts to HTTP response.
- Standardized Responses: Use
response.WriteAPISuccess,response.WriteAPICreatedandresponse.WriteAPIErrorfor all HTTP responses. - Testing: Mock repositories in service tests; mock services in handler tests; use integration tests for repositories.
- All required folders and files created
- Interfaces defined for repository, service, handler
- DTOs defined for all data transfer
- Repository returns raw SQL errors only
- Service maps errors to APIError and contains business logic
- Handler converts APIError to HTTP responses
- Router maps endpoints to handler methods
- Module wires everything together as shown above
- Tests written for service, handler, and repository
internal/template_block/
├── dto/
├── handler/
├── interfaces/
├── module/
├── repository/
├── router/
└── service/
func NewTemplateBlockModule(db *sql.DB) http.Handler {
repo := repository.NewTemplateBlockRepository(db)
service := service.NewTemplateBlockService(repo)
handler := handler.NewTemplateBlockHandler(service)
return router.NewTemplateBlockRouter(handler)
}interfaces/template_block_repository.interface.go: Repository contractinterfaces/template_block_service.interface.go: Service contract- Handler contract (if needed for testing)
- Repository returns raw SQL errors only
- Service maps errors to APIError and contains business logic
- Handler converts APIError to HTTP responses
- Router maps endpoints to handler methods
- Module wires everything together as shown above
- All required folders and files created
- Interfaces defined for repository, service
- DTOs defined for all data transfer
- Repository returns raw SQL errors only
- Service maps errors to APIError and contains business logic
- Handler converts APIError to HTTP responses
- Router maps endpoints to handler methods
- Module wires everything together as shown above
- Tests written for service, handler, and repository
See internal/user/ and internal/gym/ for working examples.