Files
skills/skills/solid/references/architecture.md
T
mathiasandClaude Opus 5 56a947b2a0 refactor(plugin)!: ship as a Claude Code plugin, one dir link per harness
Skills move to skills/<name>/SKILL.md and the repo gains both manifests, so
it is now a marketplace hosting exactly one plugin whose root is the repo
root (source: "."). Claude Code installs it from git; no symlinks involved.

Why: install.sh wired the library into every consuming repo as an ABSOLUTE
symlink to $CHECKOUT_DIR. The checkout moved three times, most recently when
local-dev@1966d5a dropped the embedded .skills/ copy, and nothing migrated
the links — 243 dangling links across 12 repos, hidden from git by the
**/.claude/skills/ ignore rule. Worse, ~/dev spans two path roots
(/Users/mathias on the iCloud-shared Macs, /home/mathias on koala), so an
absolute link stored there is broken on one side by construction. Re-running
the installer cannot fix that; only removing the mechanism can.

install.sh keeps the harnesses with no plugin system and drops from 42 links
per harness to one directory link, via the convergent ~/.agents/skills
convention that Pi and OpenCode both read:

  ~/.agents/skills          -> $CHECKOUT_DIR/skills
  ~/.config/crush/skills    -> ~/.agents/skills
  ~/.config/opencode/skills -> ~/.agents/skills

wire_claude_repo is deleted outright rather than gated behind a flag — on a
mixed-root fleet it cannot be made correct. Antigravity and Vibe targets go
too; both unused. CHECKOUT_DIR keeps its host-local default deliberately:
pointing it into ~/dev would put a git working tree inside iCloud with two
concurrent writers.

Taskfile drops five near-identical wiring loops (they had already drifted)
and delegates to install.sh. `task check` gains frontmatter validation, a
stray-root-entry gate, and manifest/tag version parity — the loose
secret-hygiene.md sat unloadable for months because nothing checked layout.

Verified: claude plugin validate . passes clean; a probe skill loaded as
skills:zzprobe through --plugin-dir; task check green at 42 skills.

Refs: #23
Bump-Type: minor

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BeAp5LTscnz2W7Ubt4eJ6m
2026-08-23 14:29:51 +02:00

8.5 KiB

Software Architecture

The Goal of Architecture

Enable the development team to:

  1. Add features with minimal friction
  2. Change existing features safely
  3. Remove features cleanly
  4. Test features in isolation
  5. Deploy independently when possible

Architectural Principles

1. Vertical Boundaries (Features/Slices)

Organize by feature, not by technical layer.

BAD: Layer-first
src/
  controllers/
    UserController.ts
    OrderController.ts
  services/
    UserService.ts
    OrderService.ts
  repositories/
    UserRepository.ts
    OrderRepository.ts

GOOD: Feature-first
src/
  users/
    UserController.ts
    UserService.ts
    UserRepository.ts
  orders/
    OrderController.ts
    OrderService.ts
    OrderRepository.ts

Why: Changes to "users" feature stay in users/. High cohesion within features.

2. Horizontal Boundaries (Layers)

Separate concerns into layers with clear dependencies.

┌──────────────────────────────────────┐
│           Presentation               │  UI, Controllers, CLI
├──────────────────────────────────────┤
│           Application                │  Use Cases, Orchestration
├──────────────────────────────────────┤
│             Domain                   │  Business Logic, Entities
├──────────────────────────────────────┤
│          Infrastructure              │  Database, APIs, External
└──────────────────────────────────────┘

3. The Dependency Rule

Dependencies point INWARD.

Infrastructure → Application → Domain
      ↓               ↓            ↓
   (outer)        (middle)      (inner)
  • Inner layers know NOTHING about outer layers
  • Domain has zero dependencies on infrastructure
  • Use interfaces to invert dependencies
// Domain defines the interface (inner)
interface UserRepository {
  save(user: User): Promise<void>;
  findById(id: UserId): Promise<User | null>;
}

// Infrastructure implements it (outer)
class PostgresUserRepository implements UserRepository {
  save(user: User): Promise<void> {
    // SQL here
  }
}

// Domain service uses the interface
class UserService {
  constructor(private repo: UserRepository) {} // Depends on abstraction
}

4. Contracts

Interfaces define boundaries between components.

// The contract
interface PaymentGateway {
  charge(amount: Money, card: CardDetails): Promise<ChargeResult>;
  refund(chargeId: string): Promise<RefundResult>;
}

// Multiple implementations possible
class StripeGateway implements PaymentGateway { }
class PayPalGateway implements PaymentGateway { }
class MockGateway implements PaymentGateway { }  // For tests

5. Cross-Cutting Concerns

Concerns that span multiple features: logging, auth, validation, error handling.

Options:

  • Middleware/interceptors
  • Decorators
  • Aspect-oriented approaches
  • Base classes (use sparingly)
// Middleware approach
class LoggingMiddleware {
  handle(request: Request, next: Handler): Response {
    console.log(`Request: ${request.path}`);
    const response = next(request);
    console.log(`Response: ${response.status}`);
    return response;
  }
}

6. Conway's Law

"Organizations design systems that mirror their communication structure."

Implication: Team structure affects architecture. Align both intentionally.


Common Architectural Styles

Layered Architecture

Traditional layers: Presentation → Business → Persistence

Pros: Simple, well-understood Cons: Can become a "big ball of mud" without discipline

Hexagonal Architecture (Ports & Adapters)

Domain at center, adapters around the edges.

        ┌─────────────────────┐
        │     HTTP Adapter    │
        └─────────┬───────────┘
                  │
┌─────────────────▼─────────────────┐
│              DOMAIN                │
│   ┌─────────────────────────┐     │
│   │      Business Logic      │     │
│   │      Use Cases           │     │
│   └─────────────────────────┘     │
└─────────────────┬─────────────────┘
                  │
        ┌─────────▼───────────┐
        │   Database Adapter   │
        └─────────────────────┘

Ports: Interfaces defined by the domain Adapters: Implementations that connect to the outside world

Clean Architecture

Similar to Hexagonal, with explicit layers:

  1. Entities - Enterprise business rules
  2. Use Cases - Application business rules
  3. Interface Adapters - Controllers, Presenters, Gateways
  4. Frameworks & Drivers - Web, DB, External interfaces

Feature-Driven Structure (Frontend)

src/
  features/
    auth/
      components/
        LoginForm.tsx
        SignupForm.tsx
      hooks/
        useAuth.ts
      services/
        authService.ts
      types/
        auth.types.ts
      index.ts  # Public API
    checkout/
      components/
      hooks/
      services/
      types/
      index.ts
  shared/
    components/  # Truly shared UI
    hooks/       # Truly shared hooks
    utils/       # Truly shared utilities

Feature-Driven Structure (Backend)

src/
  modules/
    users/
      domain/
        User.ts
        UserRepository.ts  # Interface
      application/
        CreateUser.ts      # Use case
        GetUser.ts         # Use case
      infrastructure/
        PostgresUserRepo.ts
      presentation/
        UserController.ts
        UserDTO.ts
    orders/
      domain/
      application/
      infrastructure/
      presentation/
  shared/
    domain/        # Shared value objects
    infrastructure/ # Shared infra utilities

The Walking Skeleton

Start with a minimal end-to-end slice:

  1. Thinnest possible feature that touches all layers
  2. Deployable from day one
  3. Proves the architecture works

Example walking skeleton for e-commerce:

  • User can view ONE product (hardcoded)
  • User can add it to cart
  • User can "checkout" (just logs)

From there, flesh out each feature fully.


Testing Architecture

┌────────────────────────────────────────────┐
│            E2E / Acceptance Tests          │  Few, slow, high confidence
├────────────────────────────────────────────┤
│            Integration Tests               │  Some, medium speed
├────────────────────────────────────────────┤
│              Unit Tests                    │  Many, fast, isolated
└────────────────────────────────────────────┘

Test by layer:

  • Domain: Unit tests (most tests here)
  • Application: Integration tests with mocked infra
  • Infrastructure: Integration tests with real dependencies
  • E2E: Critical paths only

Architecture Decision Records (ADRs)

Document significant decisions:

# ADR 001: Use PostgreSQL for persistence

## Status
Accepted

## Context
We need a database. Options: PostgreSQL, MongoDB, MySQL

## Decision
PostgreSQL for:
- ACID compliance
- Team familiarity
- JSON support for flexibility

## Consequences
- Need PostgreSQL expertise
- Schema migrations required
- Excellent query capabilities

Red Flags in Architecture

  • Circular dependencies between modules
  • Domain depending on infrastructure
  • Framework code in business logic
  • No clear boundaries between features
  • Shared mutable state across modules
  • "Util" or "Common" packages that grow forever
  • Database schema driving domain model