Strategic architecture, tactical design, and testable code principles (SOLID, Clean Architecture, Design Patterns, Testable Design)
71
89%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Strategic architecture principles for boundaries, dependencies, and layered system design.
Dependency Rule: Dependencies point inward only. Inner layers know nothing about outer layers.
Layers (inner → outer): Entities → Use Cases → Interface Adapters → Frameworks & Drivers
See references/dep-inward-only.md for layer definitions and examples.
Output: Classify as entity, use case, adapter, or framework decision.
Ask:
Output: Dependency violations with corrective actions.
Checklist:
Example:
Violation: Entity imports ORM decorator from infrastructure.
Refactor: Define entity as plain object; map to ORM in repository (adapter layer).Output: Use case interface with input/output ports.
Template:
// Use Case (application layer)
interface CreateOrderUseCase {
execute(input: CreateOrderInput): Promise<CreateOrderOutput>
}
// Input/Output ports (defined by use case)
interface CreateOrderInput {
userId: string
items: OrderItem[]
}
interface CreateOrderOutput {
orderId: string
status: string
}Use cases:
Output: Adapter interfaces (ports) and implementations.
Example:
// Port (defined by use case layer)
interface IOrderRepository {
save(order: Order): Promise<void>
findById(id: string): Promise<Order | null>
}
// Adapter (infrastructure layer)
class PostgresOrderRepository implements IOrderRepository {
async save(order: Order): Promise<void> {
// Map entity to ORM, persist
}
}Output: Framework integrations isolated in outermost layer.
Keep frameworks (Express, NestJS, TypeORM, React) in the infrastructure/adapter layer:
Example:
BAD: Entity uses @Entity decorator from TypeORM.
GOOD: Entity is plain TypeScript; repository maps to TypeORM in infrastructure.Output: ADR with rationale, alternatives, and risks.
Template:
Decision: Extract authentication into separate bounded context.
Rationale: Auth has independent lifecycle and team ownership.
Alternatives:
- Keep in monolith (simpler, tightly coupled)
- Partial boundary (YAGNI, easier to extract later)
Chosen: Full bounded context (clear ownership, independent deployment)
Risks: Network calls add latency; requires distributed transaction handling.BAD: Module A imports B and B imports A.
GOOD: Extract shared contract/module and invert dependencies.
BAD: Entity imports ORM decorators, framework types, or infrastructure.
GOOD: Entities are plain objects; adapters handle framework mapping.
BAD: Controller validates, calculates, and persists data.
GOOD: Controller calls use case; use case orchestrates business logic.
BAD: Use case instantiates concrete PostgresRepository.
GOOD: Use case depends on IRepository interface; DI provides implementation.
BAD: Add full hexagonal architecture "in case" of future DB migration.
GOOD: Solve current need; refactor when trigger appears (YAGNI).
# Find dependency direction violations
rg -n "import.*infrastructure.*from.*domain|import.*adapter.*from.*entity" src# Find circular dependencies
nx graph# Find framework leakage into domain
rg -n "@Entity|@Injectable|@Component" src/domainDependencies: inward-only · acyclic · data crossing boundaries · no framework imports
Components: screaming architecture · stable dependencies
Boundaries: cost awareness · defer decisions · service internal architecture
Entities: purity · rich not anemic · encapsulate invariants · value objects · no persistence awareness
Use Cases: isolation · explicit dependencies · orchestrates not implements · input/output ports · no presentation logic · transaction boundary
Adapters: gateway abstraction · thin controller · mapper translation · presenter formats
Frameworks: DI at edge · domain purity · ORM in infrastructure · web in infrastructure