Base code to create new another repository
- Clean and normalize data from multiple sources
- Prepare data for banking reports
- Support extensible data ingestion (DB, Queue, etc.)
- Follow layered / hexagonal architecture
Request/message flow follows the hexagonal layering the root README documents (Config → Provider → Repository → Service), with HTTP and messaging as separate inbound adapters converging on the same domain service:
flowchart TB
subgraph Inbound Adapters
HTTP[HTTP Handler<br/>gin]
KAFKA[Kafka Consumer]
end
DTO[DTO<br/>request/response shape]
ENTITY[Domain Entity<br/>entity.Account]
SERVICE[Domain Service<br/>ports.AccountService]
REPO[Repository<br/>ports.AccountRepository]
MODEL[DB Model<br/>models.Account]
DB[(Database<br/>GORM / Postgres driver)]
CACHE[(Redis<br/>ports.Cache)]
HTTP -.bypassed today.-> DTO
DTO --> ENTITY
HTTP --> ENTITY
KAFKA --> ENTITY
ENTITY --> SERVICE
SERVICE --> REPO
REPO --> MODEL
MODEL --> DB
SERVICE -.wired at startup, unused.-> CACHE
| Layer | File(s) | Responsibility | Status |
|---|---|---|---|
| HTTP | http/server.go, http/handler/account-handler.go |
gin engine, route registration, request binding | Functional |
| DTO | http/dto/account-dto.go |
Request/response shape, decoupled from the domain entity | Empty file — handler binds JSON straight to entity.Account, so the DTO boundary the base README calls for doesn't exist yet |
| Entity | domain/entity/account.go |
Core domain model: {ID, Username} |
Functional (minimal) |
| Service | domain/services/account-service.go |
Business logic behind ports.AccountService; Save() |
Save calls repository.Create and unconditionally returns nil — a create failure can't reach the HTTP layer, and ports.AccountRepository.Create has no error return at all, so the interface itself needs widening before this can be fixed |
| Repository | adapters/repository/account-repository.go |
Implements ports.AccountRepository; entity ⇄ model mapping |
Create, toModels, toDomain all panic("unimplemented") — not functional |
| DB Model | database/models/account_models.go |
GORM struct, maps to table account |
Functional (minimal) |
| DB Provider | database/provider/postgres.go |
Opens the DB connection, pool sizing | Named postgres.go and exported as NewMySQLClient, but opens gorm.io/driver/mysql — while docker-compose.yml provisions Postgres. Pick one driver and rename to match before wiring the repository up. |
| Cache | adapters/cache/redis.go |
Implements ports.Cache (get/set/delete with TTL) |
Connected at startup in application.go then discarded (_ = redisCache) — not used by AccountService |
| Composition root | internal/application/application.go |
Wires config → adapters → service → servers, starts everything | Functional |
| Entrypoint | cmd/server/main.go |
Process entrypoint | Functional |
Create environment variables:
cp .env.example .envUpdate your local configuration in .env
Run the initialization script:
sh init.shdocker compose up -d- Implement repository adapters
-
Location:
internal/adapters/consumer -
Steps:
-
Add a new consumer:
{name}Consumer.go -
Define input DTOs in the
/dtofolder
-
Define service interface:
- File:
internal/domain/ports/services.go
- File:
-
Implement service logic:
- Folder:
internal/domain/services
- Folder:
-
Inputs & outputs:
- Use DTOs from
internal/adapters/httpif the service is HTTP-based
- Use DTOs from
For database or external storage operations:
-
Define repository interface:
internal/domain/ports/repositories.go
-
Create adapter struct:
internal/adapters/repositories
-
Implement repository logic
Config
|
DB Provider
|
Repository (Storage)
|
Service (Use Case)- Define database models in:
internal/adapters/database/models- Note: if your table want to define is SQL please update file init.sql your SQL script
Pattern:
<domain>.<entity>.<event>.<version>Rules:
- all lowercase
- use
.to separate levels,-inside a word (never mix.and_, it breaks Kafka JMX metric names) - event name is past tense (it is a fact that already happened)
- always add
v1so you can introduce a breaking schema later - the first part = the owning service's domain. Only that service may produce to it.
- Write unit tests for services and repositories
- Mock external dependencies
- Run tests using standard Go tooling
- Run
golangci-lint runfor ensure correct syntax
- Docker-based deployment
- Environment-driven configuration
- CI/CD friendly
- Write tests for all new features
- Follow existing code structure
- Code reviews are mandatory
- Repository owner / admin
- Project team members