.claude/skills/architecture-principles/SKILL.md
--- name: architecture-principles description: Software architecture principles and design patterns: SOLID, layered architecture, Repository/Service/Factory/Strategy patterns, dependency injection, code organization, and anti-patterns to avoid. Use when designing new modules, reviewing architecture, planning complex features, or deciding on folder structure and dependency boundaries. Do NOT use for code quality checks or security audits. --- # Architecture Principles Skill **Purpose**: Define
npx skillsauth add softmg/product-tracker .claude/skills/architecture-principlesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Purpose: Define architectural standards and design patterns for the project.
// ✅ Good - Single responsibility
UserRepository:
findById(id) // DB logic only
UserService:
validateUser(user) // Business logic only
// ❌ Bad - Multiple responsibilities
User:
save() // DB logic
validate() // Business logic
render() // Presentation — three reasons to change
┌─────────────────────────────┐
│ Presentation Layer │ UI, Views, User Interface
├─────────────────────────────┤
│ Application Layer │ Use Cases, Application Logic
├─────────────────────────────┤
│ Domain Layer │ Business Logic, Entities
├─────────────────────────────┤
│ Data Layer │ Data Access, Storage
├─────────────────────────────┤
│ Infrastructure Layer │ External Services, I/O
└─────────────────────────────┘
Rules:
Abstracts data access logic behind an interface so business logic doesn't depend on storage details.
// Contract — what operations are available
UserRepository (interface/protocol/trait):
findById(id) → User or null
save(user)
delete(id)
// Concrete implementation — one per storage backend
DatabaseUserRepository implements UserRepository:
constructor(db)
findById(id) → db.query("SELECT ... WHERE id = ?", id)
save(user) → db.exec("INSERT OR UPDATE ...", user)
// Business logic only knows the interface, not the implementation
UserService:
constructor(repo: UserRepository) // injected
Encapsulates business logic and orchestrates dependencies.
UserService:
constructor(userRepo, notificationService)
registerUser(data):
1. validateRegistrationData(data) // guard
2. user = buildUserFromInput(data) // domain logic
3. userRepo.save(user) // persistence
4. notificationService.sendWelcome(user) // side effect
return user
Creates objects without hardcoding the concrete class at the call site.
PaymentProcessorFactory:
create(type):
if type == "stripe" → return StripeProcessor()
if type == "paypal" → return PayPalProcessor()
else → raise UnknownProcessorError(type)
Encapsulates interchangeable algorithms behind a common interface.
// Contract
ValidationStrategy (interface):
validate(value) → bool
// Implementations
EmailValidation implements ValidationStrategy:
validate(email) → matchesEmailPattern(email)
PasswordValidation implements ValidationStrategy:
validate(password) → length >= 8 AND hasUpperCase AND hasDigit
// Usage — caller doesn't know which strategy is used
Validator:
constructor(strategy: ValidationStrategy)
check(value) → strategy.validate(value)
Benefits:
// ✅ Good - dependencies come in from outside
UserController:
constructor(userService, logger) // injected — easy to test/swap
// ❌ Bad - dependencies created internally
UserController:
constructor():
this.userService = new UserService() // hardcoded — hard to test
this.logger = new ConsoleLogger() // hardcoded — hard to swap
Use domain-specific error types instead of generic ones — callers can then handle each case explicitly.
// Domain error types (name them after what went wrong)
ValidationError(message, field?) extends BaseError
NotFoundError(resource, id) extends BaseError
UnauthorizedError(message) extends BaseError
// Central handler dispatches by type
ErrorHandler:
handle(error):
if error is ValidationError → respond 400, show field
if error is NotFoundError → respond 404
if error is UnauthorizedError → respond 401
else → respond 500, log details privately
log(error.type, error.message)
// ✅ Good - one place to read all config (env vars, files, flags)
config:
environment = env("APP_ENV", default="development")
storage.type = env("STORAGE_TYPE", default="local")
logging.level = env("LOG_LEVEL", default="info")
features.maxRetries = env("MAX_RETRIES", default=3)
// ❌ Bad - config scattered across modules
// storage.go: storageType = env("STORAGE_TYPE")
// logger.py: logLevel = env("LOG_LEVEL")
// retry.ts: maxRetries = env("MAX_RETRIES")
// → hard to find all config keys, easy to forget defaults
Best for large applications where features are relatively independent
src/
├── features/
│ ├── authentication/
│ │ ├── auth.service.ts
│ │ ├── auth.store.ts
│ │ ├── auth.types.ts
│ │ └── auth.utils.ts
│ ├── users/
│ │ ├── user.service.ts
│ │ ├── user.types.ts
│ │ └── user.utils.ts
│ └── notifications/
├── shared/
│ ├── components/
│ ├── utils/
│ └── types/
└── config/
Advantages:
Best for smaller applications or when layers are more important than features
src/
├── services/
├── repositories/
├── models/
├── utils/
└── types/
Advantages:
/\
/ \ E2E Tests (few)
/----\
/ \ Integration Tests (some)
/--------\
/ \ Unit Tests (many)
/____________\
One class that knows/does too much
Tangled, unstructured code with unclear flow
Hardcoded values without explanation
// ❌ Bad
if user.age > 18 { ... }
// ✅ Good
MINIMUM_AGE = 18
if user.age > MINIMUM_AGE { ... }
Module A depends on B, B depends on A
Optimizing before identifying actual bottlenecks
When reviewing architecture:
Signs you need architectural changes:
Note: This skill should be consulted by agents when making architectural decisions or reviewing system design.
documentation
Task tracking and plan management. Used by planner to create plans and persist tasks, by orchestrator to read tasks and update progress, by documenter to create completion reports, and by any agent to log non-critical issues.
development
Create, edit, evaluate, and package agent skills. Use when building a new skill from scratch, improving an existing skill, running evals to test a skill, benchmarking skill performance, optimizing a skill's description for better triggering, reviewing third-party skills for quality, or packaging skills for distribution. Not for using skills or general coding tasks.
development
Simple implementation workflow - code, test, document. Use when user invokes /implement, wants to create code with automatic testing and documentation, or for simple single-purpose tasks that don't need planning.
development
Security best practices covering authentication, input validation, API security, secrets management, data protection, and OWASP Top 10. Use when implementing auth flows, API endpoints, file uploads, or any feature touching passwords, tokens, PII, or sensitive data. Do NOT use for code style reviews or architecture decisions.