S

Software Architecture Skill

Implements Clean Architecture, SOLID principles, and proven design patterns for building robust, maintainable software systems. Guides architectural decisions, layer separation, dependency management, and pattern selection.

SkillCommunitydevelopmentv1.0.0MIT
0 views0 copies

Description

This skill guides architectural decisions using Clean Architecture, SOLID principles, and established design patterns. It helps structure codebases for maintainability, testability, and scalability with clear layer separation and dependency rules.

Instructions

When making architectural decisions, apply these frameworks:

Clean Architecture Layers

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│           Frameworks & Drivers          │  Express, React, PostgreSQL
│  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”  │
│  │      Interface Adapters           │  │  Controllers, Presenters, Gateways
│  │  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”  │  │
│  │  │     Application Logic       │  │  │  Use Cases, Application Services
│  │  │  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”  │  │  │
│  │  │  │   Domain Entities     │  │  │  │  Business rules, Value objects
│  │  │  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜  │  │  │
│  │  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜  │  │
│  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Dependency Rule: Arrows point INWARD only.
Inner layers never import from outer layers.

Project Structure

src/
  domain/              # Entities, value objects, domain events
    entities/
    value-objects/
    events/
    repositories/      # Repository INTERFACES (not implementations)
  application/         # Use cases, application services
    use-cases/
    services/
    dto/               # Data transfer objects
  infrastructure/      # Implementations of interfaces
    database/          # Repository implementations
    external-apis/
    messaging/
  presentation/        # Controllers, routes, serializers
    http/
    graphql/
    websocket/

Design Pattern Selection Guide

ProblemPatternWhen to Use
Multiple types need same interfaceStrategyPayment processors, validators
Object creation is complexFactoryCreating entities with dependencies
Need to notify multiple consumersObserver/EventsDomain events, webhooks
Step-by-step processTemplate MethodReport generation, ETL pipelines
Need to add behavior without modifyingDecoratorLogging, caching, auth middleware
Complex object constructionBuilderAPI responses, query construction
Single instance neededSingletonConfig, database pool, logger
Transform data through stagesPipelineData processing, middleware chains

Implementation Example: Repository Pattern

// Domain layer: Interface (no dependency on database) interface UserRepository { findById(id: string): Promise<User | null>; save(user: User): Promise<void>; findByEmail(email: string): Promise<User | null>; } // Infrastructure layer: Implementation class PostgresUserRepository implements UserRepository { constructor(private db: Pool) {} async findById(id: string): Promise<User | null> { const result = await this.db.query( 'SELECT * FROM users WHERE id = $1', [id] ); return result.rows[0] ? this.toDomain(result.rows[0]) : null; } async save(user: User): Promise<void> { await this.db.query( 'INSERT INTO users (id, email, name) VALUES ($1, $2, $3) ON CONFLICT (id) DO UPDATE SET email = $2, name = $3', [user.id, user.email, user.name] ); } private toDomain(row: any): User { return new User(row.id, row.email, row.name); } } // Application layer: Use case class CreateUserUseCase { constructor( private userRepo: UserRepository, // Depends on interface, not Postgres private emailService: EmailService, ) {} async execute(input: CreateUserInput): Promise<User> { const existing = await this.userRepo.findByEmail(input.email); if (existing) throw new DuplicateEmailError(input.email); const user = User.create(input); await this.userRepo.save(user); await this.emailService.sendWelcome(user); return user; } }

Rules

  • Dependencies always point inward — domain never imports from infrastructure
  • Domain entities must not depend on frameworks, ORMs, or external libraries
  • Use dependency injection — never instantiate dependencies with new inside business logic
  • Repository interfaces live in the domain layer; implementations live in infrastructure
  • Use cases should orchestrate, not contain business rules (those belong in entities)
  • Prefer composition over inheritance
  • Apply YAGNI — do not add patterns "just in case"; add them when complexity demands it
  • For small projects (<10 files), this is overkill — use simpler flat structure
  • Each layer should be independently testable with mocks for outer layers
  • Document architectural decisions using ADRs (Architecture Decision Records)

Examples

User: How should I structure my Express API? Action: Apply Clean Architecture layers, suggest project structure, implement repository pattern for data access

User: Which design pattern should I use for multiple payment providers? Action: Recommend Strategy pattern, show interface + implementations for each provider

User: My service file is 500 lines, how do I break it up? Action: Identify responsibilities, extract into domain entities + use cases + repository, show the refactored structure

Community

Reviews

Write a review

No reviews yet. Be the first to review this template!

Similar Templates