AI Skill Report Card

Designing Software Architecture

A-87·Sep 13, 2026·Source: Web

Software Architecture Design

14 / 15

When asked to architect a system, produce these six artifacts in order:

  1. Architecture Layer Diagram (presentation → application → domain → infrastructure)
  2. Module Relation Map (which modules depend on which, and why)
  3. Event Flow (producers, event names, consumers, delivery guarantees)
  4. API Flow (request → gateway → service → response, including auth)
  5. Deployment Flow (CI/CD → environments → runtime topology)
  6. Folder Structure (concrete, per-module, matching the layers above)

Never output just prose. Always ground the design in these six sections, using diagrams-as-text (ASCII or mermaid) plus explanation.

Recommendation
Add a third example covering a greenfield microservices design at large scale to show variance beyond monolith/checkout scenarios
14 / 15

Progress:

  • Clarify domain scope and bounded contexts
  • Define architecture layers (presentation/app/domain/infra)
  • Identify modules and draw dependency relations (must be acyclic)
  • Decide sync vs async boundaries → design event flow for async parts
  • Design API contracts (REST/gRPC/GraphQL) and request lifecycle
  • Design deployment topology (containers, orchestration, scaling policy)
  • Generate folder structure matching modules and layers
  • Validate: no circular deps, no god-modules, single responsibility per service/module

Step Details

1. Bounded Contexts First Before drawing anything, list the core domains (e.g., Order, Payment, Inventory, User). Each becomes a module or service boundary. Never let one module own two unrelated domains.

2. Architecture Layers Use a strict layering per module:

Presentation  → controllers, REST/GraphQL handlers, DTOs
Application   → use cases, orchestrators, command/query handlers
Domain        → entities, value objects, domain services, domain events
Infrastructure→ repositories, external clients, message brokers, ORM

Dependency rule: outer layers depend on inner layers, never reverse (Dependency Inversion via interfaces/ports).

3. Module Relations Draw as a directed graph. Flag and eliminate cycles. Shared logic goes into a shared-kernel or common module — never cross-import between domain modules directly.

4. Event Flow For anything crossing service/module boundaries asynchronously:

[Order Service] --OrderCreated--> [Event Bus/Kafka] --> [Payment Service]
                                                    --> [Inventory Service]

Specify: event name, payload schema, delivery guarantee (at-least-once/exactly-once), idempotency strategy on consumers.

5. API Flow

Client --> API Gateway --> Auth Middleware --> Rate Limiter --> Service Controller
       --> Application Layer (use case) --> Domain Layer --> Infra (DB/external)
       <-- Response DTO <-- Application <-- Domain

6. Deployment Flow

Git Push --> CI (lint/test/build) --> Container Registry --> CD
        --> Staging (smoke test) --> Production (rolling/blue-green)
Runtime: API Gateway --> Load Balancer --> Service Pods (auto-scaled) --> Managed DB/Cache/Broker

7. Folder Structure Mirror layers and modules exactly. Example for a modular monolith (Node/TS style, adapt per stack):

src/
  modules/
    order/
      presentation/
        order.controller.ts
      application/
        create-order.usecase.ts
        order.query-handler.ts
      domain/
        order.entity.ts
        order.events.ts
        order.repository.interface.ts
      infrastructure/
        order.repository.ts
        order.mapper.ts
    payment/
      ... (same shape)
  shared/
    events/
      event-bus.ts
    kernel/
      base-entity.ts
  config/
  bootstrap/
Recommendation
Include a brief 'bad output' contrast (e.g., a flawed architecture with circular deps) to sharpen the good-vs-bad example quality
17 / 20

Example 1: Input: "Design architecture for an e-commerce checkout system with order, payment, and inventory."

Output:

  • Layers: standard 4-layer per module (presentation/application/domain/infrastructure)
  • Module Relation: Order depends on Payment (via port/interface) and Inventory (via port); Payment and Inventory do not depend on Order (inverted via events to avoid coupling)
  • Event Flow: OrderCreated → Kafka → Payment.Reserve, Inventory.Reserve; PaymentConfirmed/InventoryReservedOrder.Confirm; saga/orchestrator pattern for rollback on failure
  • API Flow: POST /checkout → Gateway → Auth → CheckoutControllerCreateOrderUseCase → emits domain event → returns 202 Accepted with order tracking id
  • Deployment: 3 independently deployable services behind API Gateway, each auto-scaled, Kafka as shared broker, Postgres per service (database-per-service pattern)
  • Folder Structure: as shown in Quick Start, one top-level module per bounded context

Example 2: Input: "Refactor a monolith user-management module that's grown too large."

Output:

  • Diagnosis: identify hidden bounded contexts inside (e.g., Authentication, Profile, Permissions) — split into 3 modules
  • Module Relation: Authentication is upstream (no deps); Profile and Permissions depend on Authentication via interface, not concrete class
  • Event Flow: UserRegistered emitted by Authentication, consumed by Profile (create profile) and Permissions (assign default role) — decouples sync creation logic
  • API Flow: unchanged externally (facade UserController composes the three use cases) to avoid breaking API contracts
  • Deployment: stays in-process for now (modular monolith), but module boundaries are drawn so each can be extracted to a microservice later without domain rewrite
  • Folder Structure: split modules/user/ into modules/authentication/, modules/profile/, modules/permissions/
Recommendation
Consider trimming some Best Practices/Pitfalls overlap since several points restate the same dependency-inversion principle
  • Always design ports/interfaces in domain layer; concrete implementations live in infrastructure — enables testability and swapping providers.
  • Prefer async events for cross-domain side effects; use sync API calls only within the same bounded context or for strict read-after-write needs.
  • Every service boundary implies: its own datastore (or schema), independent deployability, and an explicit contract (API/event schema) — no shared database across services.
  • Design for idempotency on every event consumer (dedupe key, upsert semantics).
  • Include observability in deployment flow by default: structured logging, tracing (correlation id propagated through API and event flows), metrics/alerts.
  • Version APIs and event schemas from day one (/v1/..., schema registry) — production-grade means backward compatibility is planned, not retrofitted.
  • Keep the folder structure output literal and copy-pasteable — architects implement faster from concrete trees than abstract descriptions.
  • Don't design a "shared domain module" that multiple bounded contexts import directly — this recreates monolith coupling with extra steps.
  • Don't skip the event flow section just because a system looks simple — even simple systems benefit from explicit async boundaries for scalability.
  • Don't propose deployment topology without addressing scaling policy, health checks, and rollback strategy — "deploy to Kubernetes" alone is not production-grade.
  • Don't let API layer talk directly to infrastructure (e.g., controller calling ORM directly) — always route through application/domain layers.
  • Don't output folder structures that don't match the module/layer design stated earlier in the same response — they must be consistent.
  • Don't default to synchronous chained API calls between services for workflows that span multiple domains — this creates fragile temporal coupling; use events/sagas instead.
0
Grade A-AI Skill Framework
Scorecard
Criteria Breakdown
Quick Start
14/15
Workflow
14/15
Examples
17/20
Completeness
18/20
Format
14/15
Conciseness
13/15