Developer Tools Software Architecture 39 vues

Stop Guessing at Software Architecture! Use This Curated Arsenal

B
Bright Coding
Auteur
Stop Guessing at Software Architecture! Use This Curated Arsenal

Stop Guessing at Software Architecture! Use This Curated Arsenal

How many times have you stared at a blank IDE, paralyzed by the weight of a critical architectural decision? The microservices vs. monolith debate raging in your head. The creeping dread that your "temporary" hack will fossilize into unmaintainable legacy code. The silent scream when you realize your team's "agile" process is actually just chaos with standups.

Here's the brutal truth: most developers are winging architecture. We're self-taught cowboys building skyscrapers on sand, hoping our Jenga tower of dependencies doesn't collapse at 3 AM on a Saturday. Stack Overflow threads contradict each other. Medium articles push flavor-of-the-month frameworks. And that "senior architect" who left six months ago? Their "self-documenting code" is now an archaeological mystery.

But what if you had a battle-tested arsenal? A single source of truth curated by engineers who've actually shipped production systems at scale? Enter awesome-software-design — the GitHub repository that's quietly becoming the secret weapon of developers who are tired of architectural roulette. This isn't another listicle. It's a disciplined taxonomy of patterns, decisions, and verified design rules that separate craft from chaos. Ready to stop guessing and start engineering?

What Is awesome-software-design?

awesome-software-design is a meticulously curated knowledge base that tackles the discipline of organizing and structuring software at the code and component level. Created by QDenka and bearing the prestigious Awesome badge, this repository fills a critical gap in developer education: the messy middle between "I can code" and "I can architect systems that survive contact with reality."

The repository's genius lies in its holistic scope. Most resources focus narrowly — design patterns here, microservices there, documentation as an afterthought. QDenka's curation recognizes that software architecture is a continuous spectrum from implementation details to organizational decisions. It spans seven interconnected domains: implementation patterns, API design, decision records, documentation-as-code, architecture verification, operational case studies, and foundational books.

Why is this trending now? Three converging forces: the collapse of "architecture astronaut" culture (teams are tired of ivory-tower diagrams that never compile), the rise of platform engineering (which demands reproducible, testable architecture), and AI-assisted coding (which makes high-level design skills more valuable than ever, since LLMs handle syntax but hallucinate structure). In an era where Copilot writes your functions, your competitive advantage is architectural judgment — and this repository is the gym for building that muscle.

Key Features That Separate Craft from Chaos

Multi-Paradigm Pattern Coverage. The repository doesn't force you into a single architectural religion. Event-driven with CQRS? Check — via Watermill and patchlevel/event-sourcing. Clean Architecture with DDD? Explore wild-workouts-go-ddd-example. Classic GoF patterns? Refactoring.Guru and language-specific implementations in Java, PHP, and python↗ Bright Coding Blog-patterns">Python. You choose the tool for the problem, not the problem for the tool.

Decision Record Ecosystem. This is where awesome-software-design transcends typical awesome-lists. It doesn't just tell you what patterns exist — it gives you infrastructure for capturing why you chose them. From Michael Nygard's foundational ADR blog post to log4brains auto-generating searchable knowledge bases, from Kubernetes KEPs to Rust RFCs, you see decision-making as a first-class engineering practice.

Fitness Function Tooling. Architecture that can't be tested is faith, not engineering. The repository catalogs ArchUnit (Java), ArchUnitNET (C#), ArchUnitTS (TypeScript), konsist (Kotlin), tach (Python), and more. These aren't linters for style — they're unit tests for architecture. Enforce layer dependencies, prevent forbidden imports, validate modular boundaries in CI.

Documentation-as-Code Revolution. Static Confluence pages die; executable diagrams live. The repository features Structurizr for C4-as-code, D2 for modern declarative diagrams, Mermaid for Markdown↗ Smart Converter-native visuals, and dependency-cruiser for self-validating architecture documentation. Your docs stay synchronized with your code or your build fails. No more "the diagram shows v2 but we shipped v3 last quarter."

War Stories from the Trenches. Theory without practice is entertainment. The operational case studies section delivers curated, concise postmortems from Figma's CRDT-based multiplayer, Discord's Cassandra-to-ScyllaDB migration, Shopify's modular monolith strategy, and Cloudflare's Rust proxy replacing Nginx. These aren't vanity blog posts — they're decision narratives with measurable outcomes.

Use Cases: Where This Repository Saves Your Sanity

Scenario 1: The Greenfield Trap. You're starting a new project. The team is energized. Someone suggests microservices "because Netflix." You pause, open awesome-software-design, and discover Shopify's modular monolith case study — how they deconstructed without distributing prematurely. You propose a Clean Architecture monolith with clear bounded contexts, deferring service extraction until telemetry proves the need. Six months later, your team ships features while the microservices team is still debugging their service mesh.

Scenario 2: The Legacy Archaeology Expedition. You've inherited a codebase where "architecture" means "whatever compiled last Tuesday." You introduce dependency-cruiser to visualize the dependency tangle, use adr/madr to document incremental improvements, and apply Fitness Function-Driven Development to prevent further erosion. Each PR now includes an architecture test ensuring new code respects the recovery boundaries you're establishing.

Scenario 3: The Distributed System Nightmare. Your event-driven platform has phantom messages, inconsistent read models, and a Kafka topic topology that resembles modern art. You study Event Modeling for visual design, implement CQRS with Watermill for reliable Pub/Sub, and reference Designing Data-Intensive Applications for consistency trade-offs. The system stabilizes because you designed with patterns, not against them.

Scenario 4: The Team Scaling Crisis. Your startup grew from 5 to 50 engineers. Conway's Law is weaponizing your org chart against your codebase. You apply Team Topologies principles from the books section, use C4 Model diagrams to create shared mental models, and establish ADR rituals for cross-team decisions. Architecture becomes a social technology, not just a technical one.

Step-by-Step Installation & Setup Guide

Since awesome-software-design is a curated knowledge repository rather than a single tool, here's how to integrate its ecosystem into your workflow:

1. Clone and Bookmark the Repository

# Clone for local reference and contribution
git clone https://github.com/QDenka/awesome-software-design.git

# Or simply star and watch for updates
# Visit: https://github.com/QDenka/awesome-software-design

2. Set Up Architecture Verification (Choose Your Stack)

For Java/Kotlin Projects:

# Gradle dependency for ArchUnit
# build.gradle
dependencies {
    testImplementation 'com.tngtech.archunit:archunit-junit5:1.2.0'
}

# Or for Kotlin with konsist
# build.gradle.kts
dependencies {
    testImplementation("com.lemonappdev:konsist:0.13.0")
}

For TypeScript/JavaScript↗ Bright Coding Blog Projects:

# Install dependency-cruiser for validation and visualization
npm install --save-dev dependency-cruiser

# Initialize configuration
npx depcruise --init

# Run validation against architecture rules
npx depcruise src --config .dependency-cruiser.js

For Python Projects:

# Install tach for module boundary enforcement
pip install tach

# Initialize and configure boundaries
tach mod

# Verify no forbidden imports exist
tach check

3. Initialize Decision Records

# Install log4brains for ADR management
npm install -g log4brains

# Initialize ADR repository
log4brains init

# Create your first decision record
log4brains adr new "Adopt CQRS for order management"

# Preview generated knowledge base
log4brains preview

4. Configure Documentation-as-Code

# Install D2 for declarative diagrams
# macOS/Linux
curl -fsSL https://d2lang.com/install.sh | sh -s --

# Create your first architecture diagram
cat > architecture.d2 << 'EOF'
direction: right

users: {
  shape: person
  label: Users
}

api: API Gateway {
  style.fill: "#e1f5fe"
}

service: Order Service {
  command: Command Handler
  query: Query Handler
}

db: {
  command_db: Command DB (PostgreSQL↗ Bright Coding Blog)
  query_db: Read DB (Elasticsearch)
}

users -> api -> service.command -> db.command_db
service.query -> db.query_db
EOF

# Compile to SVG
d2 architecture.d2 architecture.svg

5. Integrate into CI Pipeline

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

on: [push, pull_request]

jobs:
  verify:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      # Run architecture tests
      - name: Run ArchUnit tests (Java)
        if: hashFiles('**/pom.xml') != ''
        run: ./mvnw test -Dtest=*ArchTest
      
      # Validate dependencies (TypeScript)
      - name: Check module boundaries
        if: hashFiles('package.json') != ''
        run: npx depcruise src --config .dependency-cruiser.js
      
      # Verify Python module boundaries
      - name: Tach check
        if: hashFiles('pyproject.toml') != ''
        run: pip install tach && tach check
      
      # Ensure ADRs are updated for significant changes
      - name: Check ADR coverage
        run: |
          if git diff --name-only HEAD~1 | grep -q "src/"; then
            test $(find docs/adr/ -name '*.md' -mtime -7 | wc -l) -gt 0 || 
            echo "WARNING: No recent ADRs found for code changes"
          fi

REAL Code Examples from the Ecosystem

The awesome-software-design repository curates tools with production-hardened patterns. Here are concrete implementations from its referenced projects:

Example 1: ArchUnit Architecture Test (Java)

From the TNG/ArchUnit ecosystem, this enforces Clean Architecture layer dependencies:

package com.example.architecture;

import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.lang.ArchRule;
import com.tngtech.archunit.library.Architectures;
import org.junit.jupiter.api.Test;

public class CleanArchitectureTest {

    // Import all classes from the compiled output
    private final JavaClasses classes = new ClassFileImporter()
        .importPackages("com.example.myapp");

    @Test
    void domainShouldNotDependOnInfrastructure() {
        // Define Clean Architecture layers with explicit allowed dependencies
        Architectures.LayeredArchitecture architecture = Architectures
            .layeredArchitecture()
            .consideringAllDependencies()
            // Define layers by package patterns
            .layer("Domain").definedBy("..domain..")
            .layer("Application").definedBy("..application..")
            .layer("Infrastructure").definedBy("..infrastructure..")
            // Enforce dependency direction: Domain -> nothing (inner circle)
            .whereLayer("Domain").mayNotAccessAnyLayer()
            // Application may only access Domain
            .whereLayer("Application").mayOnlyAccessLayers("Domain")
            // Infrastructure may access Application and Domain
            .whereLayer("Infrastructure").mayOnlyAccessLayers("Application", "Domain");

        // This will FAIL the build if any class violates these rules
        architecture.check(classes);
    }

    @Test
    void entitiesShouldNotUseFrameworkAnnotations() {
        // Prevent JPA/Hibernate annotations in domain entities
        // keeping them framework-agnostic as Clean Architecture demands
        ArchRule noFrameworkAnnotationsInDomain = noClasses()
            .that().resideInAPackage("..domain..")
            .should().dependOnClassesThat()
            .haveNameMatching("javax\\.persistence\\..*")
            .orShould().dependOnClassesThat()
            .haveNameMatching("org\\.hibernate\\..*");

        noFrameworkAnnotationsInDomain.check(classes);
    }
}

What this guards against: The silent creep of @Entity and @Column annotations into your domain model, creating hidden coupling that makes testing painful and framework migration impossible. This test fails the build before the technical debt compounds.

Example 2: Dependency-Cruiser Configuration (TypeScript)

From dependency-cruiser, this .dependency-cruiser.js enforces hexagonal architecture boundaries:

// .dependency-cruiser.js — Architecture rules as executable policy
/** @type {import('dependency-cruiser').IConfiguration} */
module.exports = {
  forbidden: [
    {
      // CORE RULE: Domain must never depend on external layers
      name: 'domain-to-infrastructure',
      comment: 'Domain logic must remain pure — no infrastructure dependencies allowed',
      severity: 'error',
      from: { path: '^src/domain' },
      to: { 
        path: '^src/(infrastructure|application)',
        // Exception: domain events may be referenced for type safety
        pathNot: '^src/application/events/.+\.types\.ts$'
      }
    },
    {
      // ENFORCE ADAPTER PATTERN: Only infrastructure may touch external libraries
      name: 'external-lib-containment',
      comment: 'axios, prisma, redis — all isolated in infrastructure adapters',
      severity: 'error',
      from: { 
        path: '^src',
        pathNot: '^src/infrastructure'  // Only infrastructure may import externals
      },
      to: { 
        dependencyTypes: ['npm-3rd-party'],
        // Whitelist: these are considered "standard library"
        pathNot: '^(lodash|date-fns|uuid)$'
      }
    },
    {
      // PREVENT CIRCULAR DEPENDENCIES: The architecture killer
      name: 'no-circular',
      comment: 'Circular dependencies create tight coupling and prevent independent testing',
      severity: 'error',
      from: {},  // Applies to all modules
      to: { circular: true }
    },
    {
      // FEATURE ISOLATION: Bounded contexts must not intermingle
      name: 'bounded-context-isolation',
      comment: 'Orders must not directly import Inventory — use domain events or explicit APIs',
      severity: 'warn',
      from: { path: '^src/contexts/([^/]+)' },
      to: { 
        path: '^src/contexts/([^/]+)',
        pathNot: '^src/contexts/$1'  // Allow self-references only
      }
    }
  ],
  options: {
    // Output architecture violations as both text and visual graph
    doNotFollow: { path: 'node_modules' },
    reporterOptions: {
      archi: { collapsePattern: '^(src/[^/]+)' }
    }
  }
};

The power here: Your architecture rules are version-controlled, code-reviewed, and CI-enforced. No more "I didn't know we weren't supposed to import Prisma in the domain layer." The build tells you immediately.

Example 3: D2 Diagram with C4 Hierarchy

From D2 Language, this creates interactive, version-controlled architecture documentation:

direction: down

# C4 Level 1: System Context
users: {
  shape: person
  label: |md
    **Healthcare Providers**
    Doctors, nurses, administrators
  |
  style: { fill: "#08427b"; font-color: white }
}

myapp: |md
  **Clinical Trial Manager**
  Manages patient enrollment, 
  protocol compliance, and 
  regulatory reporting
| {
  shape: rectangle
  style: { fill: "#1168bd"; font-color: white; stroke: "#0b4884"; stroke-width: 2 }
}

# External systems with explicit integration patterns
email_system: {
  shape: cylinder
  label: |md
    **SendGrid**
    _Integration: SMTP/API_
  |
  style: { fill: "#999999"; font-color: white }
}

regulatory_db: {
  shape: cylinder
  label: |md
    **FDA 21 CFR Part 11**
    _Integration: Secure FTP_
  |
  style: { fill: "#999999"; font-color: white }
}

users -> myapp: Manages trials via
myapp -> email_system: Sends notifications via
myapp -> regulatory_db: Submits reports via

# Annotations for architecture decisions
explanation: |md
  **ADR-042: Why not event-driven here?**
  
  Regulatory submission requires synchronous 
  acknowledgment. Eventual consistency 
  unacceptable for FDA compliance.
| {
  shape: document
  style: { fill: "#f5f5f5"; stroke: "#666"; stroke-dash: 3 }
}

myapp -> explanation: { style.stroke-dash: 3; style.stroke: "#666" }

Compile and integrate:

# Generate SVG for documentation
d2 clinical-system.d2 docs/architecture/context.svg

# Generate PNG for presentations
d2 clinical-system.d2 docs/architecture/context.png

# Validate diagram syntax in CI
d2 fmt clinical-system.d2 --check

Why this matters: Your architecture documentation is now diffable, reviewable, and testable. When ADR-042 changes, the diagram annotation updates in the same commit. No more stale wiki pages describing version 1.0 while version 3.2 ships.

Example 4: Event Sourcing with Watermill (Go)

From ThreeDotsLabs/watermill, this shows CQRS command handling:

package main

import (
    "context"
    "log"
    
    "github.com/ThreeDotsLabs/watermill"
    "github.com/ThreeDotsLabs/watermill/message"
    "github.com/ThreeDotsLabs/watermill/message/router/middleware"
    "github.com/ThreeDotsLabs/watermill/message/router/plugin"
    "github.com/ThreeDotsLabs/watermill/pubsub/gochannel"
)

func main() {
    // In-memory Pub/Sub for development; swap for Kafka/RabbitMQ in production
    pubSub := gochannel.NewGoChannel(
        gochannel.Config{},
        watermill.NewStdLogger(false, false),
    )

    // Router orchestrates message handling with middleware pipeline
    router, err := message.NewRouter(message.RouterConfig{}, watermill.NewStdLogger(false, false))
    if err != nil {
        panic(err)
    }

    // Add reliability middleware: retry with exponential backoff
    router.AddMiddleware(
        middleware.Recoverer,           // Panic recovery — don't crash on handler bugs
        middleware.Retry{
            MaxRetries:      3,
            InitialInterval: time.Second,
            Logger:          watermill.NewStdLogger(false, false),
        }.Middleware,
        middleware.CorrelationID,       // Trace requests across async boundaries
    )

    // Plugin: ensure graceful shutdown on SIGTERM
    router.AddPlugin(plugin.SignalsHandler)

    // Handler: process PlaceOrder commands
    // "orders.commands" is the topic; handler idempotency is YOUR responsibility
    router.AddHandler(
        "place_order_handler",
        "orders.commands",      // Subscribe to command topic
        pubSub,
        "orders.events",        // Publish resulting events
        pubSub,
        func(msg *message.Message) ([]*message.Message, error) {
            // Deserialize command — validate business invariants
            cmd := PlaceOrderCommand{}
            if err := json.Unmarshal(msg.Payload, &cmd); err != nil {
                return nil, err // Dead letter queue handles poison messages
            }

            // Execute domain logic: aggregate enforces invariants
            order, err := domain.PlaceOrder(cmd.CustomerID, cmd.Items)
            if err != nil {
                return nil, err // Validation errors are domain errors, not panics
            }

            // Emit event: this becomes the source of truth
            event := OrderPlaced{
                OrderID:    order.ID,
                CustomerID: order.CustomerID,
                Total:      order.Total,
                OccurredAt: time.Now().UTC(),
            }
            
            payload, _ := json.Marshal(event)
            return []*message.Message{message.NewMessage(watermill.NewUUID(), payload)}, nil
        },
    )

    // Start processing — blocks until context cancellation
    if err := router.Run(context.Background()); err != nil {
        log.Fatal(err)
    }
}

Critical insight: The middleware pipeline separates technical concerns (retries, correlation IDs, recovery) from business logic (order validation, aggregate construction). This is the essence of the repository's philosophy: structure that separates what changes at different rates.

Advanced Usage & Best Practices

Compose verification tools strategically. Don't choose between ArchUnit and dependency-cruiser — use both. ArchUnit validates semantic layer constraints; dependency-cruiser visualizes and enforces module topology. Defense in depth for architecture.

Evolve your fitness functions. Start with broad rules ("domain can't import infrastructure"), then tighten based on pain points. When a bug escapes due to missing event validation, add a rule: "all event handlers must have validator imports." Your architecture tests should grow with your understanding of failure modes.

ADR rituals beat ADR perfection. A brief MADR template completed in 15 minutes beats a comprehensive template abandoned after three hours. The repository's curated ADR tools emphasize low friction — log4brains auto-generates sites, adr-manager provides web UI, e-adr embeds in source code. Choose the tool your team will actually use.

Diagram at multiple zoom levels. Use C4's four levels: Context (who uses this?), Container (what are the deployable units?), Component (what are the major code structures?), Code (how do classes interact?). The repository's Structurizr and D2 tooling support this hierarchy. Never show a CEO class diagrams; never show a developer system context for debugging.

Comparison with Alternatives

Dimension awesome-software-design Generic "Awesome" Lists Architecture Courses Consulting Frameworks
Scope Curated, interconnected 7-domain taxonomy Single-topic aggregation ("Awesome Go", "Awesome React") Fixed curriculum, often dated Proprietary, vendor-locked
Practicality Production tools with case studies Often hobby projects, unmaintained Academic exercises Generic, not your stack
Decision Support ADR/RFC ecosystem with real examples None Theoretical trade-off analysis Expensive, slow engagement
Verification Fitness function tooling catalog None Manual code review checklists Custom, non-transferable
Cost Free, open-source Free, variable quality $500-$5000+ $50K-$500K+
Community Velocity GitHub PRs, issues, active curation Stale, abandoned lists common Annual updates Dependent on consultant availability

The verdict: Courses teach you to think; this repository gives you tools to enforce that thinking at scale. Consulting gives you answers; this gives you the methodology to generate your own. Generic lists collect; this curates with architectural intent.

FAQ

Q: Is awesome-software-design a framework I install? A: No — it's a curated knowledge base linking to production tools, case studies, and literature. Think of it as your architecture librarian, not a library itself.

Q: Which language ecosystem is best covered? A: Multi-language by design. Java/Kotlin (ArchUnit, konsist), TypeScript/JavaScript (dependency-cruiser, ArchUnitTS), Go (arch-go, go-cleanarch, Watermill), Python (tach, diagrams), PHP (arkitect, pest-plugin-arch), Ruby (packwerk), C# (ArchUnitNET). The repository explicitly avoids language chauvinism.

Q: How do I convince my team to adopt architecture testing? A: Start with one invariant that recently caused pain. Did a production incident trace to a forbidden database import in domain logic? Write that as an ArchUnit test. Pain-driven adoption beats mandate-driven resistance.

Q: What's the difference between ADRs and RFCs? A: ADRs (Architecture Decision Records) capture past decisions with context and consequences — historical documentation. RFCs (Request for Comments) propose future changes for community feedback — design process. The repository includes both: adr/madr for decisions, Rust RFCs and next.js↗ Bright Coding Blog/discussions/categories/rfc">Next.js RFCs for proposals.

Q: Can small teams benefit from this, or is it "enterprise only"? A: Small teams benefit most. The overhead of architecture discipline scales sub-linearly; the cost of chaos scales exponentially. A 3-person team using MADR and dependency-cruiser prevents the "we'll fix it later" accumulation that kills startups.

Q: How often should architecture be reviewed? A: Continuously, not quarterly. Fitness functions in CI provide daily feedback. ADRs are written per significant decision. Case studies are reviewed when facing analogous problems. The repository's tooling enables this rhythm; it doesn't require heavy ceremony.

Q: Is this replacing my existing architecture documentation? A: It's evolving it. Static Confluence pages become executable D2 diagrams. Meeting decisions become version-controlled ADRs. Tribal knowledge becomes failing tests. The repository provides the tooling for this transformation.

Conclusion

Software architecture isn't a phase you complete before coding — it's a continuous discipline of structuring decisions. The awesome-software-design repository gives you what scattered blog posts, fragmented tooling docs, and expensive consultants cannot: a unified, curated, actionable map of proven practices.

I've watched teams transform from "hope and pray" deployment strategies to confidence rooted in verifiable constraints. The difference isn't intelligence — it's access to the right tools and the wisdom to compose them. This repository is that access, democratized.

Your next move is simple: star the repository, browse the section that matches your current pain point, and implement one fitness function this week. Not next quarter. Not after you read another book. This week. Because every day without architectural guardrails is a day you're shipping lottery tickets instead of software.

The patterns are proven. The tools are production-ready. The only question is whether you'll engineer your architecture or inherit your accidents. Choose deliberately. Star awesome-software-design now and start building systems that outlast your tenure.

Commentaires 0

Aucun commentaire pour l'instant. Soyez le premier à réagir !

Laisser un commentaire