Skip to content
Back to skills

Awesome Software Design Patterns

CSecurity

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

  • 6 stars
  • 0 votes
  • 0 copies
  • 6 views
  • Added September 8, 2026
developmentjavascripttypescriptgojavaphpbashsqlreactnodeexpress

Works with

  • cli
  • api

Security analysis

C67/100
  • criticalPipes output to a shell interpreter
  • mediumUses curl or wget to download content
  • criticalDownloads and executes remote scripts — classic supply chain attack
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned September 8, 2026

npx -y skills add Aradotso/design-skills --skill awesome-software-design-patterns --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Awesome Software Design Patterns?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Awesome Software Design Patterns
[![Security: C — Skills Directory](https://www.skillsdirectory.com/api/skills/aradotso-awesome-software-design-patterns/badge)](https://www.skillsdirectory.com/skills/aradotso-awesome-software-design-patterns)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: awesome-software-design-patterns
description: Curated guide for implementing design patterns, architecture decisions, and verified design rules in software projects
triggers:
  - "show me design patterns for this problem"
  - "how do I document architecture decisions"
  - "what are the best practices for API design"
  - "help me implement clean architecture"
  - "show me examples of architecture verification"
  - "how do I create architecture diagrams as code"
  - "what design pattern should I use here"
  - "help me set up ADR documentation"
---

# Awesome Software Design Patterns

> Skill by [ara.so](https://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
// 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
// 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
// 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
// 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
// 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
// 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
// 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

```go
// 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`:

```markdown
# 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

```bash
# 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

```bash
# 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`:

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

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

## Documentation as Code

### C4 Model with Structurizr DSL

```structurizr
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

```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
```

```mermaid
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:

```bash
# 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)

```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)

```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:

```bash
# 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`:

```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`:

```javascript
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:

```bash
# 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

```yaml
# .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)

```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
<?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

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

View all in development →

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.