- Clean Architecture keeps business rules independent from delivery so changes in frameworks never break core logic.
- Dependencies flow inward:
frameworks → adapters → application → domain. - Use cases expose gateways (ports); adapters implement them; frameworks wire everything together under
cmd/entrypoints. - Always decide between Quick vs Task workflow (see docs/AGENT_INSTRUCTIONS.md) based on the layer surface area you touch.
- Separation of concerns: Domain logic lives in entities/value objects; use cases orchestrate; adapters translate boundaries; frameworks provide tooling only.
- Dependency rule: Inner layers know nothing about outer layers—never import adapters or frameworks from the domain or application packages.
- Use-case centric design: Each business capability is a use case composed of interfaces, DTOs, and an implementation coordinating entities.
- Replaceable infrastructure: Treat databases, queues, CLIs, and HTTP servers as plugins behind interfaces.
- Testability first: Inner layers stay pure for deterministic tests; adapters get contract tests; frameworks covered by integration smoke tests.
| Term | Meaning | Where to document |
|---|---|---|
| Entity | Business object with invariants | internal/domain/entities + memory-bank/project.md |
| Value Object | Immutable concept identified by value (Money, Address) | internal/domain/valueobjects |
| Use Case | Application-specific workflow orchestrating entities | internal/application/usecases |
| Gateway (aka Port) | Interface the use case depends on (e.g., repository, message bus) | internal/application/interfaces/gateways |
| Adapter | Implementation of a gateway for a specific technology | internal/adapters/<tech> |
| Driver | Framework entry (HTTP server, CLI, worker) | cmd/<entry> |
| Layer | Responsibilities | Owns | Depends On |
|---|---|---|---|
| Domain (Entities, Value Objects, Domain Services) | Ubiquitous language, business invariants, pure logic | internal/domain |
none |
| Application (Use Cases) | Coordinate domain objects, enforce application policies | internal/application |
Domain + defined gateways (ports) |
| Interface Adapters | Translate between use cases and delivery (HTTP, queues, presenters) | internal/adapters |
Application interfaces |
| Frameworks & Drivers | Startup code, wiring, infrastructure SDKs | cmd, internal/infrastructure |
Adapters via interfaces |
Framework drivers such as MongoDB clients belong to this outer layer (internal/infrastructure). Repository adapters that satisfy application gateways (ports) live under internal/adapters/persistence and compose those drivers, keeping dependencies flowing inward.
. # Repository root
├── .github/ # CI/CD workflows configurations
│ └── workflows/
│ ├── ci.yml # Test and lint pipeline
│ └── cd.yml # Deploy pipeline
├── docs/
│ ├── architectures/
│ │ └── clean-architectures/
│ │ └── CLEAN_ARCHITECTURE.md # You are here (agent memory)
│ ├── AGENT_INSTRUCTIONS.md # Auto-Ops operational rules
│ └── QUICK_REFERENCE.md # Commands and workflow cheat sheet
├── src/ (reference layout)
│ ├── cmd/ # Entry points (executables)
│ │ ├── api/ # HTTP server
│ │ │ └── main.go # API server initialization
│ │ ├── worker/ # Worker for messaging system processing
│ │ │ └── main.go # Worker initialization
│ │ └── cli/ # Command line tool
│ │ └── main.go # CLI initialization
│ ├── configs/ # Configuration files (YAML, JSON, TOML)
│ │ ├── config.yaml # Generic configuration
│ │ ├── config.dev.yaml # Development environment configuration
│ │ └── config.prod.yaml # Production environment configuration
│ └── internal/ # Internal code (not exportable)
│ ├── domain/ # Pure domain layer
│ │ ├── entities/ # Business entities and models
│ │ │ └── order.go # Order struct
│ │ ├── services/ # Domain Services (business rules)
│ │ │ ├── interfaces/ # Domain Service contracts
│ │ │ │ └── i_order_service_domain.go # IOrderServiceDomain contract
│ │ │ └── implementations/ # Concrete Domain Service implementations
│ │ │ └── order_service_domain.go # Concrete Domain Service
│ │ ├── valueobjects/ # Value Objects (e.g., Money, Address)
│ │ │ └── money.go # VO for monetary values
│ │ └── events/ # (Optional) Pure domain events
│ │ └── order_created.go # Event representing an order creation
│ ├── application/ # Use cases and logic orchestration
│ │ ├── dto/ # Data Transfer Objects grouped by type
│ │ │ ├── requests/ # Request DTOs
│ │ │ │ ├── create_order_request.go # CreateOrder request
│ │ │ │ └── list_orders_request.go # ListOrders request
│ │ │ └── responses/ # Response DTOs
│ │ │ ├── create_order_response.go # CreateOrder response
│ │ │ └── list_orders_response.go # ListOrders response
│ │ ├── interfaces/ # Use case and gateway (port) contracts
│ │ │ ├── usecases/ # Use case boundaries
│ │ │ │ ├── i_create_order_usecase.go # ICreateOrderUseCase contract
│ │ │ │ └── i_list_orders_usecase.go # IListOrdersUseCase contract
│ │ │ └── gateways/ # Interfaces for external infrastructure
│ │ │ └── i_order_repository.go # Repository contract
│ │ ├── usecases/ # Use Case implementations
│ │ │ ├── create_order_usecase.go # CreateOrderUseCase implementation
│ │ │ └── list_orders_usecase.go # ListOrdersUseCase implementation
│ ├── adapters/ # Input/output adapters
│ │ ├── http/ # HTTP/REST adapters
│ │ │ ├── handlers/ # Handlers invoking Use Cases
│ │ │ │ └── order_handler.go # Concrete REST handler
│ │ │ └── router.go # HTTP routing
│ │ ├── sqs/ # AWS SQS queue adapters
│ │ │ └── order_events.go # SQS event processing
│ │ ├── aws_lambda/ # AWS Lambda adapters
│ │ │ └── lambda_handler.go # Lambda handler
│ │ ├── grpc/ # gRPC adapters (optional)
│ │ │ ├── proto/ # .proto files for gRPC
│ │ │ └── server.go # gRPC server
│ │ └── persistence/ # Repository implementations (gateways)
│ │ └── mongodb/
│ │ └── order_repository.go # Implements application gateway using Mongo client
│ ├── infrastructure/ # Concrete infrastructure implementations
│ │ ├── config/ # Configuration loader
│ │ │ └── loader.go # Loads config.yaml files
│ │ ├── mongodb/ # MongoDB client (framework driver)
│ │ │ └── client.go # Initializes MongoDB connection & collections
│ │ ├── aws/ # Generic AWS SDK
│ │ │ ├── sqs/ # SQS client/wrapper
│ │ │ │ └── client.go # Message enqueueing and consumption
│ │ │ └── s3/ # S3 client/wrapper
│ │ │ └── client.go # Object upload/download
│ │ ├── cache/ # Cache implementation (Redis)
│ │ │ └── client.go # Redis client
│ │ ├── logger/ # Logger configuration (Zap, Logrus…)
│ │ │ └── zap.go # Logger initialization
│ │ └── metrics/ # Prometheus instrumentation and metrics
│ │ └── prometheus.go # Exposes metrics
│ └── shared/ # Cross-cutting helpers (see Shared Directory Policy)
│ ├── errors/ # Error helpers kept infrastructure-agnostic
│ │ └── error.go # Common error definitions
│ └── utilities/ # Stateless utilities without outer-layer deps
│ └── datetime_util.go # General helper
├── scripts/ # Auxiliary scripts (migrations, seeds, deploy)
│ ├── migrate.sh # Database migrations via script
│ ├── seed.sh # Populate initial data
│ └── docker-entrypoint.sh # Custom Docker entrypoint
├── migrations/ # Versioned MongoDB migrations
│ └── 20250529_create_orders_collection.js
├── tests/ # Automated tests
│ ├── units/ # Unit tests
│ ├── integrations/ # Integration tests (MongoDB, SQS)
├── Makefile # Build, test, lint, run shortcuts
├── Dockerfile # Docker image definition for the application
├── docker-compose.yml # Service orchestration for local dev
├── README.md # Quick start guide and overview
└── go.mod / go.sum # Go module definitions and dependenciesThe src/ tree above is the canonical structure agents should follow when creating new modules. Adjust paths if the concrete project uses different roots but preserve layer isolation.
- Repository contracts and other gateways (ports) stay under the application package (
internal/application/interfaces/gateways) so the domain layer remains free of external dependencies. - Keep
internal/sharedlimited to neutral utilities (for example, error formatting) that do not import outer-layer packages; avoid moving domain or adapter logic into that area. - The tree uses Go naming and files as reference. For other stacks, keep conceptual equivalents (for example,
app/domain,lib/application) while preserving dependency direction. - File names shown with Go conventions (
main.go,i_interface.go) are illustrative; adapt extensions and naming patterns to the idioms of your stack (e.g.,*.ts,*.py, dropping thei_prefix if not used). - The
internal/domain/eventsfolder is optional and captures pure domain events (e.g.,OrderCreated) that represent business facts without coupling to infrastructure. If the project does not use events, omit the folder entirely.
src/
├── app/ # Application layer (use cases, DTOs, gateways)
│ ├── use-cases/ # Business orchestrators
│ ├── dto/ # Request/response DTOs
│ └── gateways/ # Interfaces consumed by use cases
├── domain/ # Entities, value objects, domain services
├── adapters/ # HTTP controllers, queue consumers, presenters
├── infrastructure/ # Drivers and SDK wrappers (DB clients, HTTP)
└── shared/ # Cross-cutting helpers (see policy)
Mirror the same directional dependencies: infrastructure → adapters → app → domain.
- Scope: Only cross-cutting helpers that are both stateless and infrastructure-agnostic belong under
internal/shared. - Dependency rule: Code in
sharedmust depend exclusively on language standard libraries or inner layers; never import adapters, infrastructure SDKs, or framework packages. - Promotion rule: When a helper starts requiring outer-layer details (e.g., database clients, HTTP encoders), move it to the appropriate adapter or infrastructure package instead of expanding
shared. - Documentation: Record any intentional exceptions in the decision log to keep a traceable justification for deviations.
Disclaimer: I know that in Go it's not idiomatic to prefix interface names with 'I', but I prefer to use this pattern because it has always provided me with faster file identification when using my search of development IDE.
| Component | Responsibility | Input/Output | Accesses Repositories | Contains Business Logic |
|---|---|---|---|---|
Use Case (application layer internal/application/usecases) |
Orchestrates an end-to-end system action. Coordinates transactions, calls repositories and domain services | DTOs (input and output) | Yes | Only minimal orchestration logic. Rich business rules live in entities and domain services |
Domain Service (domain layer internal/domain/services) |
Encapsulates business logic that doesn’t fit well in a single entity or involves multiple aggregates | Domain objects | Not directly. Ideally operates on already loaded entities | Yes. Pure domain business rules |
cmd/api/main.goboots the HTTP server, builds dependencies, and registers routes.- An HTTP request hits
internal/adapters/http/handlers/order_handler.go. - The handler maps the payload into a DTO from
internal/application/dto/requestsand calls theCreateOrderUseCaseinterface. - The use case coordinates entities (
internal/domain/entities/order.go) and invokes repository gateways (ports). - The persistence adapter in
internal/adapters/persistence/mongodbuses the infrastructure client to translate gateways (ports) into MongoDB operations. - The response DTO is turned into an HTTP payload by the presenter and returned.
- Model domain-published events as pure objects (
internal/domain/events) and deliver them to outbound adapters (queue, broker) through gateways defined in the application layer. - For inverse flows (e.g., message processing), keep the drivers under
cmd/worker, translate the payload into a DTO, and call the same use case that would serve the synchronous path. - Prefer handling transactional consistency inside the application layer, coordinating multiple gateways around the same use case. When the backend lacks transactions, document compensations or guarantees in the decision log.
- Capture relevant concurrency limits (locks, idempotency, deduplication) within the use case that requires them and reflect the contract in gateway tests.
- Read conventions: Load
memory-bank/project.mdand check relevant sections indocs/AGENT_INSTRUCTIONS.mdbefore touching code. - Assess complexity: If the change touches only one adapter or docs → Quick path; if it modifies use cases, domain rules, or multiple layers → Task path.
- Define gateways first: When new infrastructure is needed, add interfaces under
internal/application/interfaces/gatewaysbefore implementing adapters. - Keep dependency direction: Inner layers must not import packages from outer layers. Add new DTOs or interfaces rather than pushing infrastructure inward.
- Update memory: Capture significant architecture decisions or deviations in this file or
memory-bank/changelog.md.
- Entities: Keep constructors validating invariants; expose behaviour methods; avoid exporting mutable fields.
- Use cases: Accept interfaces + DTOs; return DTOs or error objects; encapsulate transactions or orchestrations.
- Adapters: Split handler (IO) and presenter (formatting). For queues, keep message parsing separate from use case invocation. Gateway implementations (e.g.,
persistence/mongodb/order_repository.go) live here and satisfy the contracts defined by the application layer. - Infrastructure: Provide factories to construct clients; hide SDK types behind internal structs; supply configuration via
configs/*.yaml; expose low-level drivers (MongoDB client, AWS SDKs, Redis) that adapters compose. - cmd/: Main packages compose dependencies, inject adapters into use cases, and start servers/workers.
- shared/: Reserve this directory for truly generic helpers (logging setup, serialization utilities) that do not import outer layers; if a helper needs to talk to adapters or infrastructure, promote it to the respective layer.
- Unit-test entities/value objects with pure Go tests (no mocks required).
- Use table-driven tests for use cases, mocking gateways via small in-memory doubles.
- Adapters get contract tests ensuring they satisfy gateway expectations (e.g., repository behaves under success/failure cases).
- End-to-end smoke tests live under
tests/orinternal/integrationto exercise real infrastructure sparingly.
Before Implementing
- Reviewed conventions (
memory-bank/project.md) and workflows (docs/AGENT_INSTRUCTIONS.md). - Identified affected layer(s) and chosen Quick vs Task path.
- Declared/updated required gateways and DTOs.
During Implementation
- Dependencies flow inward only.
- Adapters remain thin; business logic pushed to use cases or domain services.
- Configurable values read through configuration layer, not hardcoded.
Before Requesting Review
- Tests updated/passing per layer touched.
- Documentation adjusted (this file, QUICK_REFERENCE, AGENT_INSTRUCTIONS if workflows shift).
- Memory-bank context + quick changes recorded where required.
- Entities importing infrastructure packages (violates dependency rule).
- Use cases returning raw infrastructure types (e.g., Mongo models or HTTP responses).
- Handlers performing business logic instead of invoking use cases.
- Configuration or credentials hard-coded in adapters or domain logic.
- Skipping gateway definitions (ports), causing tight coupling between use cases and adapters.
| Date | Decision | Notes |
|---|---|---|
| 2025-02-03 | Adopt Clean Architecture layering for AI agents | Canonical structure captured in this memory bank |
| 2025-02-04 | Auto-Ops workflows govern change process | Quick vs Task selection tied to layer scope |
Update the table when major architectural decisions or exceptions are made.
- Document deviations if the real project structure diverges from the canonical tree.
- Record integration-specific guidelines (e.g., message schemas, API contracts) once stabilized.
- Clarify concurrency and transaction boundaries for domain services when first implemented.
- docs/AGENT_INSTRUCTIONS.md — automation workflow and mode selection
- docs/QUICK_REFERENCE.md — shortcut commands and complexity matrix
- memory-bank/project.md — project-specific conventions and terminology