MCPInstall

awesome-software-design-patterns

Curated guide for implementing design patterns, architecture decisions, and verified design rules in software projects

Installs
140
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/reason-machines/design-skills --skill awesome-software-design-patterns
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads awesome-software-design-patterns automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

Awesome Software Design Patterns

Skill by ara.so — Design Skills collection.

A comprehensive resource for organizing and structuring software through proven design patterns, architecture decision records (ADRs), and automated verification rules. This skill helps you apply battle-tested design principles, document architectural decisions, and enforce design constraints through CI/CD.

What This Resource Provides

This curated collection covers:

  • Implementation Patterns & Reference Code - Production-ready examples of DDD, CQRS, Clean Architecture, Event Sourcing
  • Design Patterns - All 23 GoF patterns plus enterprise and architectural patterns
  • API & Interface Design - Industry-standard guidelines from Google, Microsoft
  • Decision Records (ADR/RFC) - Templates and real-world examples for documenting architecture decisions
  • Documentation as Code - C4 Model, Mermaid, PlantUML, and other diagram-as-code tools
  • Architecture Verification - CI-integrated tools for enforcing architecture rules (ArchUnit, Arkitect, etc.)
  • Operational Case Studies - Real-world architecture examples from Figma, Discord, Shopify, Stripe

Key Design Patterns Reference

Creational Patterns

Singleton Pattern - Ensures a class has only one instance with global access:

// PHP Example
class Database {
    private static ?Database $instance = null;
    private PDO $connection;
    
    private function __construct() {
        $this->connection = new PDO(
            $_ENV['DB_DSN'],
            $_ENV['DB_USER'],
            $_ENV['DB_PASS']
        );
    }
    
    public static function getInstance(): Database {
        if (self::$instance === null) {
            self::$instance = new Database();
        }
        return self::$instance;
    }
    
    public function getConnection(): PDO {
        return $this->connection;
    }
}

// Usage
$db = Database::getInstance();

Factory Pattern - Creates objects without specifying exact classes:

// Go Example
package notification

type Notification interface {
    Send(message string) error
}

type EmailNotification struct {
    recipient string
}

func (e *EmailNotification) Send(message string) error {
    // Send email implementation
    return nil
}

type SMSNotification struct {
    phoneNumber string
}

func (s *SMSNotification) Send(message string) error {
    // Send SMS implementation
    return nil
}

type NotificationFactory struct{}

func (f *NotificationFactory) Create(notifType, target string) Notification {
    switch notifType {
    case "email":
        return &EmailNotification{recipient: target}
    case "sms":
        return &SMSNotification{phoneNumber: target}
    default:
        return nil
    }
}

// Usage
factory := &NotificationFactory{}
notif := factory.Create("email", "user@example.com")
notif.Send("Hello World")

Builder Pattern - Constructs complex objects step by step:

// Go Example
package query

type SQLQuery struct {
    table      string
    columns    []string
    where      string
    orderBy    string
    limit      int
}

type QueryBuilder struct {
    query SQLQuery
}

func NewQueryBuilder(table string) *QueryBuilder {
    return &QueryBuilder{
        query: SQLQuery{table: table, columns: []string{"*"}},
    }
}

func (b *QueryBuilder) Select(columns ...string) *QueryBuilder {
    b.query.columns = columns
    return b
}

func (b *QueryBuilder) Where(condition string) *QueryBuilder {
    b.query.where = condition
    return b
}

func (b *QueryBuilder) OrderBy(field string) *QueryBuilder {
    b.query.orderBy = field
    return b
}

func (b *QueryBuilder) Limit(limit int) *QueryBuilder {
    b.query.limit = limit
    return b
}

func (b *QueryBuilder) Build() string {
    sql := "SELECT " + strings.Join(b.query.columns, ", ") + " FROM " + b.query.table
    if b.query.where != "" {
        sql += " WHERE " + b.query.where
    }
    if b.query.orderBy != "" {
        sql += " ORDER BY " + b.query.orderBy
    }
    if b.query.limit > 0 {
        sql += fmt.Sprintf(" LIMIT %d", b.query.limit)
    }
    return sql
}

// Usage
query := NewQueryBuilder("users").
    Select("id", "name", "email").
    Where("age > 18").
    OrderBy("created_at DESC").
    Limit(10).
    Build()

Structural Patterns

Adapter Pattern - Makes incompatible interfaces work together:

// PHP Example - Adapting legacy payment gateway to new interface
interface PaymentGateway {
    public function processPayment(float $amount, string $currency): bool;
}

class LegacyPaymentService {
    public function pay(int $amountInCents, string $currencyCode): array {
        // Legacy payment logic
        return ['status' => 'success', 'transaction_id' => 'TXN123'];
    }
}

class PaymentAdapter implements PaymentGateway {
    private LegacyPaymentService $legacyService;
    
    public function __construct(LegacyPaymentService $legacyService) {
        $this->legacyService = $legacyService;
    }
    
    public function processPayment(float $amount, string $currency): bool {
        $amountInCents = (int)($amount * 100);
        $result = $this->legacyService->pay($amountInCents, $currency);
        return $result['status'] === 'success';
    }
}

// Usage
$legacy = new LegacyPaymentService();
$adapter = new PaymentAdapter($legacy);
$success = $adapter->processPayment(99.99, 'USD');

Decorator Pattern - Adds behavior to objects dynamically:

// Go Example
package middleware

type Handler interface {
    Handle(request string) string
}

type BaseHandler struct{}

func (h *BaseHandler) Handle(request string) string {
    return "Handling: " + request
}

type LoggingDecorator struct {
    handler Handler
}

func (d *LoggingDecorator) Handle(request string) string {
    log.Printf("Before handling: %s", request)
    result := d.handler.Handle(request)
    log.Printf("After handling: %s", result)
    return result
}

type AuthDecorator struct {
    handler Handler
}

func (d *AuthDecorator) Handle(request string) string {
    if !isAuthenticated(request) {
        return "Unauthorized"
    }
    return d.handler.Handle(request)
}

// Usage - Stack decorators
handler := &BaseHandler{}
handler = &LoggingDecorator{handler: handler}
handler = &AuthDecorator{handler: handler}
result := handler.Handle("user-request")

Behavioral Patterns

Strategy Pattern - Defines a family of algorithms and makes them interchangeable:

// PHP Example - Different pricing strategies
interface PricingStrategy {
    public function calculate(float $basePrice): float;
}

class RegularPricing implements PricingStrategy {
    public function calculate(float $basePrice): float {
        return $basePrice;
    }
}

class SeasonalDiscount implements PricingStrategy {
    private float $discountPercent;
    
    public function __construct(float $discountPercent) {
        $this->discountPercent = $discountPercent;
    }
    
    public function calculate(float $basePrice): float {
        return $basePrice * (1 - $this->discountPercent / 100);
    }
}

class VIPPricing implements PricingStrategy {
    public function calculate(float $basePrice): float {
        return $basePrice * 0.80; // 20% off
    }
}

class Product {
    private float $basePrice;
    private PricingStrategy $pricingStrategy;
    
    public function __construct(float $basePrice, PricingStrategy $strategy) {
        $this->basePrice = $basePrice;
        $this->pricingStrategy = $strategy;
    }
    
    public function setStrategy(PricingStrategy $strategy): void {
        $this->pricingStrategy = $strategy;
    }
    
    public function getPrice(): float {
        return $this->pricingStrategy->calculate($this->basePrice);
    }
}

// Usage
$product = new Product(100, new RegularPricing());
echo $product->getPrice(); // 100

$product->setStrategy(new SeasonalDiscount(15));
echo $product->getPrice(); // 85

$product->setStrategy(new VIPPricing());
echo $product->getPrice(); // 80

Observer Pattern - Defines subscription mechanism to notify multiple objects:

// Go Example - Event system
package events

type Event struct {
    Name string
    Data interface{}
}

type Observer interface {
    Update(event Event)
}

type Subject struct {
    observers []Observer
}

func (s *Subject) Attach(observer Observer) {
    s.observers = append(s.observers, observer)
}

func (s *Subject) Detach(observer Observer) {
    for i, obs := range s.observers {
        if obs == observer {
            s.observers = append(s.observers[:i], s.observers[i+1:]...)
            break
        }
    }
}

func (s *Subject) Notify(event Event) {
    for _, observer := range s.observers {
        observer.Update(event)
    }
}

// Concrete observers
type EmailNotifier struct{}

func (e *EmailNotifier) Update(event Event) {
    log.Printf("Email notification for event: %s", event.Name)
}

type LogObserver struct{}

func (l *LogObserver) Update(event Event) {
    log.Printf("Logging event: %s with data: %v", event.Name, event.Data)
}

// Usage
subject := &Subject{}
subject.Attach(&EmailNotifier{})
subject.Attach(&LogObserver{})

subject.Notify(Event{
    Name: "UserRegistered",
    Data: map[string]string{"email": "user@example.com"},
})

Implementing Clean Architecture

Layered Structure

project/
├── domain/          # Business logic, entities, domain events
├── application/     # Use cases, application services
├── infrastructure/  # External dependencies (DB, API, queue)
└── interfaces/      # Controllers, CLI, API handlers

Go Example with Clean Architecture

// domain/user.go - Core business entity
package domain

import "errors"

type User struct {
    ID       string
    Email    string
    Password string
    Active   bool
}

func NewUser(email, password string) (*User, error) {
    if email == "" {
        return nil, errors.New("email required")
    }
    if len(password) < 8 {
        return nil, errors.New("password must be at least 8 characters")
    }
    return &User{
        Email:    email,
        Password: password,
        Active:   true,
    }, nil
}

type UserRepository interface {
    Save(user *User) error
    FindByEmail(email string) (*User, error)
}

// application/register_user.go - Use case
package application

import "project/domain"

type RegisterUserUseCase struct {
    repo domain.UserRepository
}

func NewRegisterUserUseCase(repo domain.UserRepository) *RegisterUserUseCase {
    return &RegisterUserUseCase{repo: repo}
}

func (uc *RegisterUserUseCase) Execute(email, password string) error {
    // Check if user exists
    existing, _ := uc.repo.FindByEmail(email)
    if existing != nil {
        return errors.New("user already exists")
    }
    
    // Create new user
    user, err := domain.NewUser(email, password)
    if err != nil {
        return err
    }
    
    // Save user
    return uc.repo.Save(user)
}

// infrastructure/postgres_user_repository.go - Implementation
package infrastructure

import (
    "database/sql"
    "project/domain"
)

type PostgresUserRepository struct {
    db *sql.DB
}

func NewPostgresUserRepository(db *sql.DB) *PostgresUserRepository {
    return &PostgresUserRepository{db: db}
}

func (r *PostgresUserRepository) Save(user *domain.User) error {
    _, err := r.db.Exec(
        "INSERT INTO users (email, password, active) VALUES ($1, $2, $3)",
        user.Email, user.Password, user.Active,
    )
    return err
}

func (r *PostgresUserRepository) FindByEmail(email string) (*domain.User, error) {
    user := &domain.User{}
    err := r.db.QueryRow(
        "SELECT id, email, password, active FROM users WHERE email = $1",
        email,
    ).Scan(&user.ID, &user.Email, &user.Password, &user.Active)
    
    if err == sql.ErrNoRows {
        return nil, nil
    }
    return user, err
}

// interfaces/http/handler.go - API layer
package http

import (
    "encoding/json"
    "net/http"
    "project/application"
)

type RegisterRequest struct {
    Email    string `json:"email"`
    Password string `json:"password"`
}

type UserHandler struct {
    registerUseCase *application.RegisterUserUseCase
}

func NewUserHandler(registerUseCase *application.RegisterUserUseCase) *UserHandler {
    return &UserHandler{registerUseCase: registerUseCase}
}

func (h *UserHandler) Register(w http.ResponseWriter, r *http.Request) {
    var req RegisterRequest
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    
    err := h.registerUseCase.Execute(req.Email, req.Password)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(map[string]string{"status": "success"})
}

Architecture Decision Records (ADR)

Basic ADR Template (MADR Format)

Create docs/adr/0001-use-postgresql-for-primary-database.md:

# 1. Use PostgreSQL for Primary Database

Date: 2024-01-15

## Status

Accepted

## Context

We need to choose a primary database for our application that will store user data, 
transactions, and business entities. Requirements:
- ACID transactions
- Complex queries with JOINs
- Strong consistency
- Open source with good community support
- Performance for 10M+ records

## Decision

We will use PostgreSQL 15+ as our primary relational database.

## Consequences

### Positive
- Strong ACID guarantees for financial data
- Excellent JSON support for flexible schemas
- Rich ecosystem of tools (PgBouncer, pg_stat_statements, pgAdmin)
- Built-in full-text search capabilities
- Easy horizontal read scaling with replicas

### Negative
- More complex to scale writes compared to NoSQL solutions
- Requires careful index management for query performance
- Backup/restore operations need planning at scale
- Vertical scaling limits eventual

### Neutral
- Team needs to maintain PostgreSQL expertise
- Will use managed service (AWS RDS) to reduce operational burden

CLI Tools for Managing ADRs

# Install adr-tools
brew install adr-tools  # macOS
apt-get install adr-tools  # Ubuntu

# Initialize ADR directory
adr init docs/architecture/decisions

# Create new ADR
adr new "Use event sourcing for order management"

# Link ADRs
adr new -l "2:Amends:partial replacement" "Use CQRS with event sourcing"

# Generate table of contents
adr generate toc > docs/architecture/decisions/README.md

Using log4brains for ADR Management

# Install log4brains
npm install -g log4brains

# Initialize in project
log4brains init

# Create new ADR interactively
log4brains adr new

# Preview documentation site
log4brains preview

# Build static site
log4brains build

Configuration .log4brains.yml:

project:
  name: "My Project"
  tz: "America/New_York"

adr:
  defaultStatus: "proposed"
  defaultTemplate: "madr"
  
output:
  path: "dist"

Documentation as Code

C4 Model with Structurizr DSL

workspace "E-Commerce System" {
    model {
        customer = person "Customer" "Places orders online"
        
        ecommerce = softwareSystem "E-Commerce Platform" {
            webapp = container "Web Application" "React SPA" "JavaScript"
            api = container "API Gateway" "REST API" "Go"
            orderService = container "Order Service" "Manages orders" "Go"
            paymentService = container "Payment Service" "Processes payments" "Go"
            database = container "Database" "Stores data" "PostgreSQL"
            messageQueue = container "Message Queue" "Event streaming" "RabbitMQ"
        }
        
        paymentGateway = softwareSystem "Payment Gateway" "External payment processor"
        
        # Relationships
        customer -> webapp "Uses"
        webapp -> api "Makes API calls to" "HTTPS/JSON"
        api -> orderService "Routes requests to"
        api -> paymentService "Routes requests to"
        orderService -> database "Reads/writes"
        orderService -> messageQueue "Publishes events"
        paymentService -> messageQueue "Subscribes to events"
        paymentService -> paymentGateway "Processes payments via"
    }
    
    views {
        systemContext ecommerce {
            include *
            autolayout lr
        }
        
        container ecommerce {
            include *
            autolayout lr
        }
        
        theme default
    }
}

Mermaid Diagrams in Markdown

# System Architecture

```mermaid
graph TB
    Client[Client Application]
    API[API Gateway]
    Auth[Auth Service]
    User[User Service]
    Order[Order Service]
    DB[(PostgreSQL)]
    Cache[(Redis)]
    
    Client -->|HTTPS| API
    API -->|Verify Token| Auth
    API -->|User Requests| User
    API -->|Order Requests| Order
    User -->|Read/Write| DB
    Order -->|Read/Write| DB
    User -->|Cache| Cache
    Order -->|Cache| Cache
sequenceDiagram
    participant U as User
    participant A as API
    participant O as Order Service
    participant P as Payment Service
    participant Q as Queue
    
    U->>A: POST /orders
    A->>O: Create Order
    O->>Q: Publish OrderCreated
    O-->>A: Order ID
    A-->>U: 201 Created
    
    Q->>P: OrderCreated Event
    P->>P: Process Payment
    P->>Q: Publish PaymentProcessed

### D2 Diagrams

Create `architecture.d2`:

```d2
title: Microservices Architecture {
  shape: text
  near: top-center
}

users: Users {
  shape: person
}

api_gateway: API Gateway {
  shape: hexagon
}

services: {
  auth: Auth Service {
    shape: rectangle
  }
  
  orders: Order Service {
    shape: rectangle
  }
  
  payments: Payment Service {
    shape: rectangle
  }
}

data: {
  postgres: PostgreSQL {
    shape: cylinder
  }
  
  redis: Redis Cache {
    shape: cylinder
  }
  
  queue: RabbitMQ {
    shape: queue
  }
}

users -> api_gateway: HTTPS
api_gateway -> services.auth: gRPC
api_gateway -> services.orders: gRPC
api_gateway -> services.payments: gRPC

services.auth -> data.postgres
services.orders -> data.postgres
services.payments -> data.postgres

services.orders -> data.queue: Publish Events
services.payments -> data.queue: Subscribe Events

services.auth -> data.redis: Cache Sessions

Generate diagrams:

# Install D2
curl -fsSL https://d2lang.com/install.sh | sh

# Generate SVG
d2 architecture.d2 architecture.svg

# Watch mode for development
d2 --watch architecture.d2 architecture.svg

Architecture Verification in CI

ArchUnit (Java)

// src/test/java/com/example/ArchitectureTest.java
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.lang.ArchRule;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.*;
import static com.tngtech.archunit.library.Architectures.layeredArchitecture;

public class ArchitectureTest {
    private final JavaClasses classes = new ClassFileImporter()
        .importPackages("com.example");
    
    @Test
    public void layerDependenciesAreRespected() {
        layeredArchitecture()
            .layer("Controllers").definedBy("..controller..")
            .layer("Services").definedBy("..service..")
            .layer("Repositories").definedBy("..repository..")
            
            .whereLayer("Controllers").mayNotBeAccessedByAnyLayer()
            .whereLayer("Services").mayOnlyBeAccessedByLayers("Controllers")
            .whereLayer("Repositories").mayOnlyBeAccessedByLayers("Services")
            
            .check(classes);
    }
    
    @Test
    public void servicesShouldNotDependOnControllers() {
        ArchRule rule = noClasses()
            .that().resideInAPackage("..service..")
            .should().dependOnClassesThat().resideInAPackage("..controller..");
        
        rule.check(classes);
    }
    
    @Test
    public void repositoriesShouldBeInterfaces() {
        ArchRule rule = classes()
            .that().resideInAPackage("..repository..")
            .and().haveSimpleNameEndingWith("Repository")
            .should().beInterfaces();
        
        rule.check(classes);
    }
}

Arkitect (PHP)

// tests/Architecture/LayersTest.php
use Arkitect\ClassSet;
use Arkitect\CLI\Config;
use Arkitect\Expression\ForClasses\HaveNameMatching;
use Arkitect\Expression\ForClasses\ResideInOneOfTheseNamespaces;
use Arkitect\Rules\Rule;

return static function (Config $config): void {
    $classSet = ClassSet::fromDir(__DIR__.'/../../src');

    $rules = [];

    // Controllers should not depend on infrastructure
    $rules[] = Rule::allClasses()
        ->that(new ResideInOneOfTheseNamespaces('App\Controller'))
        ->should(new NotDependsOnTheseNamespaces('App\Infrastructure'))
        ->because('Controllers should not know about infrastructure details');

    // Domain should not depend on anything
    $rules[] = Rule::allClasses()
        ->that(new ResideInOneOfTheseNamespaces('App\Domain'))
        ->should(new NotDependsOnTheseNamespaces(
            'App\Controller',
            'App\Infrastructure'
        ))
        ->because('Domain must remain isolated');

    // Repositories must be interfaces
    $rules[] = Rule::allClasses()
        ->that(new HaveNameMatching('*Repository'))
        ->should(new BeInterface())
        ->because('Repositories must be interfaces for DIP');

    $config
        ->add($classSet, ...$rules);
};

Run checks:

# Run architecture tests
vendor/bin/phparkitect check

# CI integration
vendor/bin/phparkitect check --config=phparkitect.php --stop-on-failure

arch-go (Go)

Create arch_test.go:

package architecture

import (
    "testing"
    
    "github.com/arch-go/arch-go/api"
    "github.com/arch-go/arch-go/api/configuration"
)

func TestArchitecture(t *testing.T) {
    config := configuration.NewConfig()
    
    // Domain should not depend on infrastructure
    config.AddRule(
        api.Rule{
            Package: "github.com/myorg/myapp/domain",
            ShouldNotDependOn: []string{
                "github.com/myorg/myapp/infrastructure",
                "github.com/myorg/myapp/interfaces",
            },
            Description: "Domain must be independent",
        },
    )
    
    // Infrastructure should not depend on interfaces
    config.AddRule(
        api.Rule{
            Package: "github.com/myorg/myapp/infrastructure",
            ShouldNotDependOn: []string{
                "github.com/myorg/myapp/interfaces",
            },
            Description: "Infrastructure should not know about interfaces",
        },
    )
    
    // Interfaces can depend on application and domain
    config.AddRule(
        api.Rule{
            Package: "github.com/myorg/myapp/interfaces",
            ShouldOnlyDependOn: []string{
                "github.com/myorg/myapp/application",
                "github.com/myorg/myapp/domain",
            },
            Description: "Interfaces should only depend on application and domain",
        },
    )
    
    result := api.CheckArchitecture(config)
    if !result.Pass {
        t.Fatalf("Architecture rules violated:\n%s", result.String())
    }
}

dependency-cruiser (TypeScript/JavaScript)

Create .dependency-cruiser.js:

module.exports = {
  forbidden: [
    {
      name: 'no-circular',
      severity: 'error',
      from: {},
      to: { circular: true }
    },
    {
      name: 'no-orphans',
      severity: 'warn',
      from: { orphan: true, pathNot: '\\.(test|spec)\\.' },
      to: {}
    },
    {
      name: 'domain-isolation',
      severity: 'error',
      from: { path: '^src/domain' },
      to: {
        path: '^src/(infrastructure|interfaces)',
        pathNot: '^src/domain'
      }
    },
    {
      name: 'no-infrastructure-in-application',
      severity: 'error',
      from: { path: '^src/application' },
      to: { path: '^src/infrastructure' }
    }
  ],
  options: {
    doNotFollow: {
      path: 'node_modules'
    },
    tsConfig: {
      fileName: 'tsconfig.json'
    }
  }
};

Run checks:

# Install
npm install --save-dev dependency-cruiser

# Validate architecture
npx depcruise --validate .dependency-cruiser.js src

# Generate graph
npx depcruise --include-only "^src" --output-type dot src | dot -T svg > architecture-graph.svg

GitHub Actions CI Integration

# .github/workflows/architecture-checks.yml
name: Architecture Verification

on: [push, pull_request]

jobs:
  architecture-tests:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Go
        uses: actions/setup-go@v4
        with:
          go-version: '1.21'
      
      - name: Run Architecture Tests
        run: go test ./tests/architecture/...
      
      - name: Check Module Dependencies
        run: |
          go install github.com/arch-go/arch-go@latest
          go test -v ./arch_test.go

Event-Driven Architecture Patterns

Event Sourcing Example (Go)

// domain/events.go
package domain

import "time"

type Event interface {
    EventType() string
    OccurredAt() time.Time
}

type OrderCreated struct {
    OrderID    string
    CustomerID string
    Amount     float64
    Timestamp  time.Time
}

func (e OrderCreated) EventType() string { return "OrderCreated" }
func (e OrderCreated) OccurredAt() time.Time { return e.Timestamp }

type OrderPaid struct {
    OrderID      string
    PaymentID    string
    Amount       float64
    Timestamp    time.Time
}

func (e OrderPaid) EventType() string { return "OrderPaid" }
func (e OrderPaid) OccurredAt() time.Time { return e.Timestamp }

// Aggregate
type Order struct {
    ID         string
    CustomerID string
    Amount     float64
    Status     string
    events     []Event
}

func (o *Order) Apply(event Event) {
    switch e := event.(type) {
    case OrderCreated:
        o.ID = e.OrderID
        o.CustomerID = e.CustomerID
        o.Amount = e.Amount
        o.Status = "pending"
    case OrderPaid:
        o.Status = "paid"
    }
    o.events = append(o.events, event)
}

func (o *Order) GetUncommittedEvents() []Event {
    return o.events
}

// Event Store
type EventStore interface {
    SaveEvents(aggregateID string, events []Event) error
    GetEvents(aggregateID string) ([]Event, error)
}

CQRS Pattern (PHP)

<?php
// Command side
namespace App\Application\Command;

class CreateOrderCommand
{
    public function __construct(
        public readonly string $orderId,
        public readonly string $customerId,
        public readonly float $amount
    ) {}
}

class CreateOrderHandler
{
    public function __construct(
        private OrderRepository $repository,
        private EventBus $eventBus
    ) {}
    
    public function handle(CreateOrderCommand $command): void
    {
        $order = new Order(
            $command->orderId,
            $command->customerId,
            $command->amount
        );
        
        $this->repository->save($order);
        
        $this->eventBus->publish(new OrderCreated

FAQ

What does the awesome-software-design-patterns skill do?

Curated guide for implementing design patterns, architecture decisions, and verified design rules in software projects

How do I install awesome-software-design-patterns?

Run npx skills add https://github.com/reason-machines/design-skills --skill awesome-software-design-patterns in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/awesome-software-design-patterns/ (personal) or .claude/skills/awesome-software-design-patterns/ (project).

Which AI agents can use awesome-software-design-patterns?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is awesome-software-design-patterns safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains awesome-software-design-patterns?

It is published by reason-machines in reason-machines/design-skills. It has 140 recorded installs.

More from reason-machines/design-skills

View all →

awesome-claude-design

reason-machines/design-skills

Open the skill to read its SKILL.md instructions.

205 installs

open-design-ai-prototyping

reason-machines/design-skills

Open the skill to read its SKILL.md instructions.

174 installs

design-md-format

reason-machines/design-skills

Open the skill to read its SKILL.md instructions.

171 installs

open-codesign-ai-design

reason-machines/design-skills

Open the skill to read its SKILL.md instructions.

171 installs

design-md-chrome-extractor

reason-machines/design-skills

Open the skill to read its SKILL.md instructions.

166 installs

claude-design-system-hooks

reason-machines/design-skills

Open the skill to read its SKILL.md instructions.

Frontend & Design165 installs

Popular code quality & workflow skills

View category →

grill-me

mattpocock/skills

A relentless interview to sharpen a plan or design.

Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick.

handoff

mattpocock/skills

Compact the current conversation into a handoff document for another agent to pick up.

domain-modeling

mattpocock/skills

Build and sharpen a project's domain model. Use when discussing codebase terminology, writing or editing a GLOSSARY.md, or recording or editing an ADR.

codebase-design

mattpocock/skills

Shared vocabulary for designing deep modules. Use when the user wants to design or improve a module's interface, find deepening opportunities, decide where a seam goes, make code more testable or AI-navigable, or when another skill needs the deep-module vocabulary.

diagnosing-bugs

mattpocock/skills

Diagnosis loop for hard bugs and performance regressions. Use when the user says "diagnose"/"debug this", or reports something broken/throwing/failing/slow.