Skip to content

Implement Architecture Decision Records (ADRs) #372

@nanotaboada

Description

@nanotaboada

Problem

Current State:
This project has made several significant architectural decisions (SQLite over other databases, Repository + Service pattern, AutoMapper, FluentValidation, in-memory caching, stadium-themed versioning), but the rationale, context, and trade-offs behind these decisions are not formally documented.

Pain Points:

  1. Learning Barrier: As a learning PoC emphasizing "clarity and best practices," new developers or learners cannot easily understand WHY certain architectural choices were made, only WHAT was implemented.

  2. Lost Context: Over time, the motivation behind decisions becomes unclear. Future contributors may:

    • Blindly accept outdated decisions that should be revisited
    • Reverse decisions without understanding their consequences
    • Repeat past mistakes by not knowing what was already considered and rejected
  3. Onboarding Friction: New contributors must reverse-engineer decisions from code, commit history, or incomplete documentation rather than reading clear rationale.

  4. Decision Reversals: When requirements change (e.g., moving from SQLite to PostgreSQL for production), there's no record of:

    • What alternatives were considered
    • What trade-offs were made
    • What circumstances would warrant revisiting the decision

Proposed Solution

Implement Architecture Decision Records (ADRs) using Michael Nygard's template to document all architecturally significant decisions in a lightweight, maintainable format.

Key Benefits:

  • Educational Value: Learners understand not just implementation patterns, but decision-making reasoning
  • Knowledge Preservation: Decisions remain accessible as team composition changes
  • Context for AI Agents: Structured ADRs provide high-value context for GitHub Copilot and other AI assistants
  • Decision Traceability: Clear audit trail of what was decided, when, and why
  • Change Confidence: Future changes informed by understanding original constraints and trade-offs
  • Best Practice Demonstration: Shows professional decision documentation for a learning resource

Timing: This is particularly valuable now because:

Suggested Approach

1. Repository Structure

Create an adr/ directory at the project root to store all ADRs:

Dotnet.Samples.AspNetCore.WebApi/
├── adr/
│   ├── README.md                                        # ADR index and navigation
│   ├── 0001-adopt-traditional-layered-architecture.md  # May be superseded by Issue #266
│   ├── 0002-use-mvc-controllers-over-minimal-api.md
│   ├── 0003-use-sqlite-for-data-storage.md
│   ├── 0004-use-uuid-as-database-primary-key.md
│   ├── 0005-use-squad-number-as-api-mutation-key.md
│   ├── 0006-use-rfc-7807-problem-details-for-errors.md
│   ├── 0007-use-fluentvalidation-over-data-annotations.md
│   ├── 0008-use-automapper-for-dto-mapping.md
│   ├── 0009-implement-in-memory-caching.md
│   ├── 0010-use-serilog-for-structured-logging.md
│   ├── 0011-use-docker-for-containerization.md
│   └── 0012-use-stadium-themed-semantic-versioning.md
├── README.md
└── ...

2. ADR Template (Michael Nygard Format)

# [NUMBER]. [TITLE]

Date: YYYY-MM-DD

## Status

[Proposed | Accepted | Deprecated | Superseded by ADR-XXXX]

## Context

What is the issue we're facing? What forces are at play (technical, political, social, project constraints)? Present facts neutrally without bias.

## Decision

We will [DECISION IN ACTIVE VOICE WITH FULL SENTENCES].

## Consequences

### Positive
- Consequence 1
- Consequence 2

### Negative
- Trade-off 1
- Limitation 1

### Neutral
- Other effect 1

3. Initial ADRs to Create

Retroactive ADRs (document existing decisions, ordered by architectural significance):

  1. 0001-adopt-traditional-layered-architecture.md: Why traditional layered architecture over Clean Architecture
  2. 0002-use-mvc-controllers-over-minimal-api.md: Why MVC controllers over ASP.NET Core Minimal API
    • Context: Explicit routing via attributes, clear separation of concerns, familiar convention for learners coming from MVC backgrounds, better testability with constructor injection
    • Trade-offs: More boilerplate than Minimal API, heavier startup overhead, Minimal API is the direction Microsoft is investing in for new projects
  3. 0003-use-sqlite-for-data-storage.md: Why SQLite over PostgreSQL/SQL Server
  4. 0004-use-uuid-as-database-primary-key.md: Why Guid (UUID v4) as the database primary key instead of a sequential integer
    • Context: Avoids leaking record counts via predictable IDs, decouples identity generation from the database, aligns with .NET defaults (Guid.NewGuid()), supports future distributed or multi-source scenarios
    • Trade-offs: Larger storage footprint than int, random GUIDs can fragment B-tree indexes (less relevant with SQLite), not human-readable; the Id is intentionally never surfaced in API URLs or responses
  5. 0005-use-squad-number-as-api-mutation-key.md: Why squadNumber is the route parameter for all mutation endpoints (GET /players/squadNumber/{n}, PUT, DELETE) instead of the internal UUID
    • Context: squadNumber is the natural, human-readable domain identifier for a player within a team; exposing the UUID in URLs would couple API consumers to an internal database concern and provide no domain value
    • Trade-offs: Squad numbers are unique only within a single team and can be reassigned between seasons; uniqueness must be enforced at the application layer (BeUniqueSquadNumber validator rule); not suitable as a stable long-term identifier across team or season boundaries
  6. 0006-use-rfc-7807-problem-details-for-errors.md: Why RFC 7807 Problem Details for all error responses
    • Context: Standardised machine-readable error format, consistent shape across all endpoints, built-in support via TypedResults.Problem / TypedResults.ValidationProblem in ASP.NET Core
    • Trade-offs: More verbose than a plain string message, consumers must understand the Problem Details schema
  7. 0007-use-fluentvalidation-over-data-annotations.md: Why FluentValidation
    • Context: Complex validation rules, separation of concerns, testability, explicit rule sets per operation (Create / Update)
    • Trade-offs: Additional dependency, not built-in to ASP.NET Core
  8. 0008-use-automapper-for-dto-mapping.md: Why AutoMapper vs manual mapping
    • Context: Reduce boilerplate, convention-based mapping
    • Trade-offs: Magic behavior, runtime performance cost, learning curve
  9. 0009-implement-in-memory-caching.md: Why IMemoryCache vs distributed cache
    • Context: Demo simplicity, single-instance deployment, fast access
    • Trade-offs: Lost on restart, not suitable for multi-instance deployments
  10. 0010-use-serilog-for-structured-logging.md: Why Serilog over Microsoft.Extensions.Logging
    • Context: Structured log output (JSON-friendly), enrichers, multiple sinks (console + file), consistent format across the cross-language comparison projects
    • Trade-offs: Additional dependency over the built-in abstractions, configuration is more involved
  11. 0011-use-docker-for-containerization.md: Why Docker + Docker Compose for local development and distribution
    • Context: Consistent dev environment, zero-config onboarding, mirrors production-like setup
    • Trade-offs: Requires Docker Desktop, adds a layer of abstraction over the host environment
  12. 0012-use-stadium-themed-semantic-versioning.md: Why football stadium names
    • Context: Memorable releases, alphabetical progression, project theme
    • Trade-offs: Non-standard, might confuse newcomers

4. Integration Points

Update CONTRIBUTING.md:
Add section on ADRs:

## Architecture Decision Records

When proposing changes that affect:
- Project structure or architecture
- Technology choices or dependencies
- Non-functional requirements (performance, security, scalability)
- Development workflows or processes

Please create an ADR in the `adr/` directory following the existing template. See `adr/README.md` for guidance.

Update README.md:
Add ADR section to documentation:

## Architecture Decisions

See [Architecture Decision Records (ADRs)](adr/README.md) for documented decisions about this project's architecture, technology choices, and development practices.

Update .github/copilot-instructions.md:
Add ADR context for AI agents:

## Architecture Decision Records (ADRs)

Load `#file:adr/README.md` when:
- User asks about architectural choices or "why we use X"
- Proposing changes to core architecture or dependencies
- Need historical context for past decisions

5. Implementation Steps

  1. Create adr/ directory structure
  2. Write adr/README.md with index, navigation, and template
  3. Create retroactive ADRs 0001-0012 for existing decisions
  4. Update CONTRIBUTING.md, README.md, and Copilot instructions
  5. Add ADR creation to PR review checklist

Important: ADR 0001 (Traditional Layered Architecture) documents the current state with full awareness that Issue #266 proposes a future change to Clean Architecture. This demonstrates proper ADR usage:

  • Capture the original decision with its context
  • Mark it as "Under Reconsideration"
  • When Clean Architecture is adopted, create a new ADR that supersedes 0001
  • Keep 0001 in the repository for historical context

6. Tooling (Optional Future Enhancement)

Consider adopting adr-tools or similar:

  • adr new "Use PostgreSQL for production": Generate new ADR with boilerplate
  • adr list: Show all ADRs with status
  • adr supersede 0003 "Use PostgreSQL": Link superseded decisions

For now, manual markdown files are sufficient given the project's simplicity.

Acceptance Criteria

  • Analysis completed: ADRs would benefit this project
  • adr/ directory created at project root
  • adr/README.md created with ADR index, template, and usage guide
  • Retroactive ADRs created for 12 existing key decisions (0001-0012)
  • CONTRIBUTING.md updated with ADR guidance for contributors
  • README.md updated to reference ADR documentation
  • .github/copilot-instructions.md updated to include ADR context loading guidance
  • All ADRs use consistent format (Michael Nygard template)
  • ADRs are written in clear, full sentences (not bullet fragments)
  • Each ADR is 1-2 pages maximum
  • CI/CD pipeline continues to pass with new documentation
  • Documentation changes committed following conventional commits format

References

ADR Resources

Related Project Documentation

  • #file:.github/copilot-instructions.md - Current AI agent instruction strategy
  • #file:CONTRIBUTING.md - Contribution guidelines to be extended with ADR process
  • #file:CHANGELOG.md - Shows evolution of decisions over time (e.g., .NET 10 upgrade)
  • #file:README.md - Project overview highlighting "clarity and best practices" focus

Related GitHub Issues

These open issues involve architectural decisions that would benefit from ADR documentation:

Note: Implementing ADRs now will establish the practice before these major architectural changes, ensuring decision rationale is captured from the start.

Example ADR Repositories

Tooling

Metadata

Metadata

Assignees

No one assigned

    Labels

    .NETPull requests that update .NET codeenhancementNew feature or requestgood first issueGood for newcomerspriority lowNice-to-have improvement. Can be deferred without blocking other work.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions