- 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 {
Auf GitHub ansehen