Application Architecture - Azure/az-prototype GitHub Wiki
Application Architecture
Overview
The application layer (app in the Layer Architecture) is unique among layers because it contains multiple sub-layers that represent distinct tiers of an application. While infrastructure and data layers delegate IaC generation to terraform-agent or bicep-agent, the application layer delegates code generation to language-specific developer agents based on the technology choices made during design.
Application Sub-Layers
Every application stage organizes code into four sub-layers, each in its own directory:
| Sub-Layer | Purpose | Directory |
|---|---|---|
| Presentation | UI pages, React components, Blazor views, client-side routing | web/ or ui/ |
| Domain (Business Logic) | Domain models, business rules, validation, workflow orchestration | services/ or domain/ |
| Data Access | Repository implementations, ORM mappings, query builders | data/ or repositories/ |
| Background | Background workers, message consumers, scheduled tasks | workers/ or functions/ |
Additionally, cross-cutting concerns live in each project root:
- Dependency injection configuration (
Program.cs/main.py) - Structured logging setup
- Health check endpoints (
/healthz) - Authentication middleware (MSAL / DefaultAzureCredential)
- Error handling middleware
- Configuration binding from environment variables
- Dockerfile and deployment configuration
Architect-to-Developer Delegation
The application-architect agent owns the entire application layer. It designs the application structure, defines sub-layer boundaries, and then delegates actual code generation to language-specific developer agents.
application-architect
|
+-- csharp-developer (C#/.NET backend + Blazor frontend)
|
+-- python-developer (Python backend)
|
+-- react-developer (React/TypeScript frontend)
|
+-- app-developer (fallback: Java, Go, Rust, etc.)
The architect:
- Receives the architecture design and infrastructure outputs
- Determines technology choices from the discovery/design context
- Defines interface contracts between sub-layers
- Assigns each sub-layer to the appropriate developer agent
- Ensures dependency injection wires all cross-layer communication
Developer Agent Sub-Layer Contracts
Each developer agent declares which sub-layers it can generate code for via the sub_layers field on its AgentContract:
| Agent | Sub-Layers | Notes |
|---|---|---|
csharp-developer |
api, business-logic, data-access, background, presentation | All 5 -- handles full-stack C#/.NET including Blazor |
python-developer |
api, business-logic, data-access, background | 4 backend layers -- no presentation (Python has no frontend framework in scope) |
react-developer |
presentation | Presentation only -- frontend communicates with backend via REST API |
app-developer |
(none declared) | Generic fallback for unsupported languages (Java, Go, Rust, etc.) |
application-architect |
presentation, api, business-logic, data-access, background | All 5 -- can generate directly when no language-specific developer matches |
Language Detection and Routing
During the build stage, the _resolve_developer_for_stage() method routes each application stage to the correct developer agent. The routing uses signal-based scoring:
Signal sources (scanned for language hints):
- Stage name (e.g.,
frontend,api-service) - Stage directory path
- Service names within the stage
- Architecture context lines mentioning the stage
Language hint map:
| Hint | Resolved Language |
|---|---|
csharp, c#, .net, dotnet, aspnet, blazor, entity-framework |
C# |
python, fastapi, flask, django |
Python |
react, typescript, next, angular, vue, frontend, spa |
React/TypeScript |
Scoring process:
- Collect text signals from the stage name, directory, service names, and architecture
- Count hint matches for each language
- Select the language with the highest score
- If no hints match (score = 0), fall back to
application-architectorapp-developer
Delegation flow (_decompose_app_stage):
- Call
_resolve_developer_for_stage()to detect the language - If a language-specific developer is found, inject sub-layer guidance into the task prompt
- If no developer matches, fall back to the
application-architect(which handles both design and code generation)
Cross-Layer Dependency Rules
Sub-layers follow strict dependency direction. Each layer depends only on the layer below it, communicating through interfaces and dependency injection:
Presentation
| (REST API calls with Bearer token)
v
API / Controllers
| (interface injection)
v
Domain / Business Logic
| (interface injection)
v
Data Access / Repositories
Rules:
- API endpoints depend on Domain services via interfaces
- Domain logic depends on Data Access repositories via interfaces
- Data Access implements repository interfaces; uses Entity Framework Core, SQLAlchemy, or Azure SDKs
- Background workers share Domain and Data Access with the API layer
- Presentation (React) communicates with the backend exclusively via REST API calls -- never accesses Azure services directly
- Interfaces are defined BEFORE implementations to enable testability and DI
- All cross-layer wiring happens in the DI configuration entry point (
Program.cs/main.py)
Project Structure Examples
C# / ASP.NET Core
src/
+-- MyApp.Api/
| +-- Program.cs # DI config, middleware, host builder
| +-- MyApp.Api.csproj # Package references
| +-- Endpoints/ # [API] Minimal API endpoint groups
| +-- Controllers/ # [API] Controllers (if controller-based)
| +-- Models/ # [API] Request/response DTOs
| +-- Services/ # [Business Logic] Interfaces + implementations
| +-- Domain/ # [Business Logic] Domain models, validation
| +-- Data/ # [Data Access] DbContext, repositories
| +-- Middleware/ # [Cross-Cutting] Error handling, auth
| +-- Extensions/ # [Cross-Cutting] Service registration
| +-- appsettings.json
| +-- Dockerfile
| +-- .env.example
+-- MyApp.Worker/ # [Background] Worker services
| +-- Program.cs
| +-- MyApp.Worker.csproj
| +-- Consumers/ # Message consumers
+-- MyApp.Functions/ # [Background] Azure Functions
| +-- Program.cs # Isolated worker host
| +-- MyApp.Functions.csproj
| +-- Functions/
+-- MyApp.Shared/
+-- MyApp.Shared.csproj
+-- Contracts/ # Shared interfaces and DTOs
Python / FastAPI
apps/
+-- api/
| +-- main.py # [Cross-Cutting] FastAPI app + DI + middleware
| +-- config.py # [Cross-Cutting] Settings from environment
| +-- endpoints/ # [API] Route definitions, request handlers
| +-- models/ # [API] Pydantic request/response models
| +-- services/ # [Business Logic] Domain logic
| +-- domain/ # [Business Logic] Domain models, validation
| +-- data/ # [Data Access] Repository pattern, ORM, queries
| +-- middleware/ # [Cross-Cutting] Error handling, auth
| +-- requirements.txt
| +-- Dockerfile
| +-- .env.example
+-- worker/ # [Background] Message consumers
| +-- main.py
| +-- consumers/
| +-- requirements.txt
+-- functions/ # [Background] Azure Functions
| +-- function_app.py # v2 programming model
| +-- requirements.txt
| +-- host.json
+-- shared/
+-- contracts.py # Shared Protocol classes and DTOs
+-- azure_clients.py # Azure SDK client factories
React / TypeScript (Presentation Layer)
apps/
+-- web/
+-- src/
| +-- main.tsx # App entry point
| +-- App.tsx # Root component with providers
| +-- auth/ # [Auth] MSAL configuration and provider
| +-- components/ # [UI] Reusable UI components
| | +-- layout/ # Layout (Header, Sidebar, Footer)
| | +-- common/ # Shared (Button, Card, Modal)
| | +-- features/ # Feature-specific components
| +-- pages/ # [Routing] Route page components
| +-- hooks/ # [State] Custom hooks (useApi, useAuth)
| +-- services/ # [API Client] Typed API calls to backend
| +-- types/ # [Contracts] TypeScript interfaces and types
| +-- utils/ # Helper functions
+-- public/
+-- index.html
+-- vite.config.ts
+-- tsconfig.json
+-- tailwind.config.js
+-- package.json
+-- .env.example
+-- Dockerfile # Multi-stage build (node -> nginx)
Azure Service Authentication
All developer agents enforce the same authentication pattern regardless of language:
- Backend services use
DefaultAzureCredential(C#:Azure.Identity, Python:azure-identity) - Frontend (React) uses MSAL (
@azure/msal-react) to acquire tokens, sent asBearerin API calls - No secrets are ever hardcoded -- all configuration via environment variables
- No connection strings with embedded keys -- use managed identity endpoints
See also: Layer Architecture for the full four-level taxonomy, Agent System for complete agent contracts and delegation chains.