Skip to content

Backend Architecture Diagrams

Visual representations of the LCO backend architecture to help understand the system structure and data flow.


System Architecture

High-Level Architecture

┌──────────────────────────────────────────────────────────────┐
│                     Frontend Layer                            │
│           (React + Vite + TypeScript)                        │
│                                                               │
│  Components → State Management → API Client                  │
└───────────────────────────┬──────────────────────────────────┘
                            │ HTTP/JSON (REST)
                            │ CORS Enabled
┌──────────────────────────────────────────────────────────────┐
│                   FastAPI Application                         │
│  ┌────────────────────────────────────────────────────────┐ │
│  │            API Layer (Routers)                         │ │
│  │  • Request validation                                  │ │
│  │  • Response serialization                              │ │
│  │  • HTTP status codes                                   │ │
│  │  • Error handling                                      │ │
│  └────────────────┬───────────────────────────────────────┘ │
│                   │ Dependency Injection                     │
│                   ↓                                           │
│  ┌────────────────────────────────────────────────────────┐ │
│  │         Schema Layer (Pydantic Models)                 │ │
│  │  • Data validation                                     │ │
│  │  • Type checking                                       │ │
│  │  • Serialization/Deserialization                       │ │
│  │  • Field aliases (camelCase ↔ snake_case)             │ │
│  └────────────────┬───────────────────────────────────────┘ │
│                   │                                           │
│                   ↓                                           │
│  ┌────────────────────────────────────────────────────────┐ │
│  │       Repository Layer (Data Access)                   │ │
│  │  • CRUD operations                                     │ │
│  │  • Query building                                      │ │
│  │  • Partition key management                            │ │
│  │  • Business logic                                      │ │
│  └────────────────┬───────────────────────────────────────┘ │
│                   │                                           │
│                   ↓                                           │
│  ┌────────────────────────────────────────────────────────┐ │
│  │         Database Layer (Cosmos Client)                 │ │
│  │  • Connection management                               │ │
│  │  • Container initialization                            │ │
│  │  • Health checks                                       │ │
│  └────────────────┬───────────────────────────────────────┘ │
└───────────────────┼──────────────────────────────────────────┘
                    │ Azure SDK
┌────────────────────────────────────────────────────────────────────┐
│              Azure Cosmos DB (NoSQL - SQL API)                     │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────────┐     │
│  │ Clients  │  │ Projects │  │  Crews   │  │   Services   │     │
│  │Container │  │Container │  │Container │  │  Container   │     │
│  └──────────┘  └──────────┘  └──────────┘  └──────────────┘     │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────────┐     │
│  │Equipment │  │CrewTrades│  │CrewMembrs│  │ServiceCrews  │     │
│  │Container │  │Container │  │Container │  │  Container   │     │
│  └──────────┘  └──────────┘  └──────────┘  └──────────────┘     │
│  ┌──────────────┐  ┌──────────────┐                              │
│  │ Estimations  │  │MaterialTakeoff│                             │
│  │  Container   │  │  Container    │                             │
│  └──────────────┘  └──────────────┘                              │
└────────────────────────────────────────────────────────────────────┘

Request/Response Flow

Complete Request Lifecycle

1. Client Request
┌─────────────────────────────────────────────┐
│ POST /api/v1/clients                        │
│ {                                           │
│   "clientCode": "ACME",                     │
│   "clientName": "ACME Corp",                │
│   "clientType": "private"                   │
│ }                                           │
└──────────────────┬──────────────────────────┘
2. FastAPI Router  │
   ↓               │
┌─────────────────────────────────────────────┐
│ @router.post("", response_model=Client)     │
│ async def create_client(                    │
│   client_data: ClientCreate,  ← Validation  │
│   repo = Depends(get_repo)    ← Injection   │
│ )                                           │
└──────────────────┬──────────────────────────┘
3. Validation      │
   ↓               │
┌─────────────────────────────────────────────┐
│ ClientCreate Pydantic Model                 │
│ • Validates types                           │
│ • Checks required fields                    │
│ • Applies constraints                       │
│ ✓ Valid → Continue                          │
│ ✗ Invalid → 422 Validation Error            │
└──────────────────┬──────────────────────────┘
4. Repository      │
   ↓               │
┌─────────────────────────────────────────────┐
│ ClientRepository.create_client()            │
│ • Transform to dict                         │
│ • Add metadata (id, timestamps)             │
│ • Set partition key                         │
│ • Call base repository                      │
└──────────────────┬──────────────────────────┘
5. Database        │
   ↓               │
┌─────────────────────────────────────────────┐
│ Cosmos DB Container                         │
│ await container.create_item(body=item)      │
│ • Stores document                           │
│ • Returns created item                      │
└──────────────────┬──────────────────────────┘
6. Response        │
   ↓               │
┌─────────────────────────────────────────────┐
│ Client Pydantic Model                       │
│ • Converts dict to model                    │
│ • Validates response                        │
│ • Serializes to JSON                        │
└──────────────────┬──────────────────────────┘
7. HTTP Response   │
   ↓               │
┌─────────────────────────────────────────────┐
│ 201 Created                                 │
│ {                                           │
│   "id": "generated-uuid",                   │
│   "clientCode": "ACME",                     │
│   "clientName": "ACME Corp",                │
│   "clientType": "private",                  │
│   "createdAt": "2025-10-09T...",            │
│   "updatedAt": "2025-10-09T..."             │
│ }                                           │
└─────────────────────────────────────────────┘

Dependency Injection Flow

How Dependencies are Resolved

Endpoint Definition
┌──────────────────────────────────────────────┐
│ @router.get("/{client_id}")                  │
│ async def get_client(                        │
│   client_id: str,            ← Path param    │
│   repo: ClientRepository = Depends(get_repo) │
│ ):                                           │
│   return await repo.get_client(client_id)    │
└──────────────────┬───────────────────────────┘
    Request comes in with client_id="123"
FastAPI sees Depends(get_repo)
┌──────────────────────────────────────────────┐
│ async def get_repo() -> ClientRepository:    │
│   container = cosmos_db.get_container(...)   │
│   return ClientRepository(container)         │
└──────────────────┬───────────────────────────┘
┌──────────────────────────────────────────────┐
│ cosmos_db.get_container("clients")           │
│ • Returns container client                   │
└──────────────────┬───────────────────────────┘
┌──────────────────────────────────────────────┐
│ ClientRepository(container)                  │
│ • Creates repository instance                │
└──────────────────┬───────────────────────────┘
Back to endpoint with repo injected
┌──────────────────────────────────────────────┐
│ async def get_client(                        │
│   client_id="123",                           │
│   repo=<ClientRepository instance>           │
│ ):                                           │
│   return await repo.get_client("123")        │
└──────────────────────────────────────────────┘

Repository Pattern Structure

Class Hierarchy and Relationships

┌─────────────────────────────────────┐
│      BaseRepository<T>              │
│      (Generic Abstract)             │
├─────────────────────────────────────┤
│ - container: ContainerProxy         │
├─────────────────────────────────────┤
│ + create(item)                      │
│ + get_by_id(id, pk)                 │
│ + update(item)                      │
│ + delete(id, pk)                    │
│ + query(sql, params)                │
│ + query_with_pagination()           │
│ + upsert(item)                      │
│ + batch_create(items)               │
│ # get_partition_key(item)  ← Abstract│
└──────────────┬──────────────────────┘
               │ Extends
┌─────────────────────────────────────┐
│    ClientRepository                 │
│    (Concrete Implementation)        │
├─────────────────────────────────────┤
│ + get_partition_key(item)           │
│ + create_client(data)               │
│ + get_client(id)                    │
│ + get_client_by_code(code)          │
│ + update_client(id, data)           │
│ + delete_client(id)                 │
│ + get_all_clients(filters)          │
│ + get_client_projects(id)           │
│ + update_project_counts(id)         │
│ + get_client_service_history(id)    │
└─────────────────────────────────────┘

Similar pattern for:
- ProjectRepository
- CrewRepository
- ServiceRepository
- ServiceCrewRepository
- EquipmentRepository

Pydantic Model Hierarchy

Schema Inheritance Pattern

For each entity (e.g., Client):

┌─────────────────────────────────────┐
│         BaseSchema                  │
│  (Common Pydantic config)           │
├─────────────────────────────────────┤
│ model_config = ConfigDict(...)      │
│ • populate_by_name=True             │
│ • use_enum_values=True              │
│ • validate_assignment=True          │
└──────────────┬──────────────────────┘
               │ Inherits
┌─────────────────────────────────────┐
│         ClientBase                  │
│  (Shared fields)                    │
├─────────────────────────────────────┤
│ - client_code: str                  │
│ - client_name: str                  │
│ - client_type: ClientType           │
│ - industry: Optional[Industry]      │
│ - contact_info: Optional[Contact]   │
└──────┬──────────┬───────────────────┘
       │          │
       │          └─────────────┐
       ↓                        ↓
┌──────────────────┐    ┌──────────────────┐
│  ClientCreate    │    │  ClientUpdate    │
│  (For POST)      │    │  (For PUT)       │
├──────────────────┤    ├──────────────────┤
│ All fields       │    │ All fields       │
│ from ClientBase  │    │ Optional         │
│ (Required)       │    │                  │
└──────────────────┘    └──────────────────┘

               ┌──────────────────┐
               │  BaseDocument    │
               │  (DB fields)     │
               ├──────────────────┤
               │ - id: str        │
               │ - created_at     │
               │ - updated_at     │
               │ - created_by     │
               │ - updated_by     │
               └────────┬─────────┘
       ┌────────────────┴─────────────────┐
       ↓                                  ↓
┌──────────────────┐            ┌──────────────────┐
│     Client       │            │  ClientDetailed  │
│  (Response)      │            │  (Extended)      │
├──────────────────┤            ├──────────────────┤
│ Inherits from:   │            │ Inherits from:   │
│ • ClientBase     │            │ • Client         │
│ • BaseDocument   │            ├──────────────────┤
├──────────────────┤            │ Additional:      │
│ Additional:      │            │ - recentProjects │
│ - client_id      │            │ - serviceHistory │
│ - type           │            │                  │
│ - project_count  │            │                  │
└──────────────────┘            └──────────────────┘

Database Schema (Cosmos DB)

Container Structure and Partition Keys

Database: lco-construction
├─ Container: Clients
│  ├─ Partition Key: /clientId
│  ├─ Document Structure:
│  │  {
│  │    "id": "uuid",
│  │    "clientId": "same-as-id",
│  │    "type": "client",
│  │    "clientCode": "ACME",
│  │    "clientName": "ACME Corp",
│  │    "clientType": "private",
│  │    "industry": "commercial",
│  │    "projectCount": 5,
│  │    "activeProjectCount": 2,
│  │    "createdAt": "2025-10-15T...",
│  │    "updatedAt": "2025-10-15T..."
│  │  }
├─ Container: Projects
│  ├─ Partition Key: /clientId (co-located with client for efficient queries)
│  ├─ Document Structure:
│  │  {
│  │    "id": "uuid",
│  │    "clientId": "client-uuid",
│  │    "projectId": "same-as-id",
│  │    "type": "project",
│  │    "projectCode": "PRJ-001",
│  │    "projectName": "Building Construction",
│  │    "projectType": "commercial",
│  │    "contractType": "lump-sum",
│  │    "status": "active",
│  │    "location": {"country": "Canada", "province": "Ontario", ...},
│  │    "year": 2025,
│  │    "quarter": "Q1",
│  │    "workingDays": 240,
│  │    "workingHours": 1920,
│  │    "createdAt": "2025-10-15T...",
│  │    "updatedAt": "2025-10-15T..."
│  │  }
├─ Container: Services
│  ├─ Partition Key: /projectId (co-located with project)
│  ├─ Document Structure:
│  │  {
│  │    "id": "uuid",
│  │    "projectId": "project-uuid",
│  │    "serviceId": "same-as-id",
│  │    "type": "service",
│  │    "serviceType": "estimation",
│  │    "status": "active",
│  │    "startDate": "2025-01-01",
│  │    "endDate": "2025-12-31",
│  │    "createdAt": "2025-10-15T..."
│  │  }
├─ Container: Crews
│  ├─ Partition Key: /crewId (self-referencing)
│  ├─ Document Structure:
│  │  {
│  │    "id": "uuid",
│  │    "crewId": "same-as-id",
│  │    "type": "crew",
│  │    "crewCode": "CONC-001",
│  │    "crewName": "Concrete Pour Crew",
│  │    "discipline": "concrete",
│  │    "manpower": [
│  │      {"tradeCode": "CONC", "laborDesignation": "Foreman", "quantity": 1},
│  │      {"tradeCode": "CONC", "laborDesignation": "Finisher", "quantity": 3}
│  │    ],
│  │    "equipment": [
│  │      {"equipmentCode": "MIX-001", "quantity": 1}
│  │    ],
│  │    "productivityFactor": 1.0
│  │  }
├─ Container: CrewTrades
│  ├─ Partition Key: /tradeCode
│  ├─ Document Structure:
│  │  {
│  │    "id": "uuid",
│  │    "tradeCode": "CARP",
│  │    "tradeName": "Carpenter",
│  │    "category": "skilled",
│  │    "description": "Carpentry work including framing and finishing"
│  │  }
├─ Container: CrewMembers
│  ├─ Partition Key: /locationKey
│  ├─ Format: country|province|tradeCode|laborDesignation
│  ├─ Document Structure:
│  │  {
│  │    "id": "uuid",
│  │    "locationKey": "Canada|Ontario|CARP|Journeyman",
│  │    "tradeCode": "CARP",
│  │    "laborDesignation": "Journeyman Carpenter",
│  │    "country": "Canada",
│  │    "province": "Ontario",
│  │    "region": "Toronto",
│  │    "year": 2025,
│  │    "quarter": "Q1",
│  │    "projectType": "commercial",
│  │    "baseRate": 45.00,
│  │    "overtimeRate": 67.50,
│  │    "doubleTimeRate": 90.00,
│  │    "tripleTimeRate": 135.00
│  │  }
├─ Container: ServiceCrews
│  ├─ Partition Key: /serviceCrewId (self-referencing)
│  ├─ Document Structure:
│  │  {
│  │    "id": "uuid",
│  │    "serviceCrewId": "same-as-id",
│  │    "serviceId": "service-uuid",
│  │    "crewId": "crew-uuid",
│  │    "crewCode": "CONC-001",
│  │    "crewName": "Concrete Pour Crew",
│  │    "quantity": 1,
│  │    "rate": 450.00,
│  │    "total": 450.00,
│  │    "indirectCosts": {
│  │      "labour": {
│  │        "employerHealthTax": 5.0,
│  │        "employmentInsurance": 3.5,
│  │        "canadaPensionPlan": 6.0,
│  │        ...
│  │      },
│  │      "equipment": {
│  │        "smallTools": 2.0,
│  │        "consumables": 1.5,
│  │        ...
│  │      }
│  │    }
│  │  }
├─ Container: Equipment
│  ├─ Partition Key: /equipmentCode
│  ├─ Document Structure:
│  │  {
│  │    "id": "uuid",
│  │    "equipmentCode": "EXC-001",
│  │    "equipmentName": "Hydraulic Excavator",
│  │    "equipmentType": "excavator",
│  │    "category": "heavy",
│  │    "unit": "day",
│  │    "baseUnitPrice": 850.00,
│  │    "description": "20-ton excavator with bucket"
│  │  }
├─ Container: Estimations
│  ├─ Partition Key: /serviceId (co-located with service)
│  ├─ Document Structure:
│  │  {
│  │    "id": "uuid",
│  │    "serviceId": "service-uuid",
│  │    "estimationId": "same-as-id",
│  │    "estimationType": "preliminary",
│  │    "totalEstimate": 1500000.00,
│  │    "breakdown": {...}
│  │  }
└─ Container: MaterialTakeoff (MTO)
   ├─ Partition Key: /projectId (co-located with project)
   ├─ Document Structure:
      {
        "id": "uuid",
        "projectId": "project-uuid",
        "serviceId": "service-uuid",
        "discipline": "electrical",
        "items": [
          {
            "description": "Conduit EMT 3/4\"",
            "quantity": 500,
            "unit": "LF",
            "unitCost": 2.50,
            "totalCost": 1250.00
          }
        ],
        "totalMaterialCost": 125000.00
      }

Why These Partition Keys?

Co-location Strategy: - Projects use /clientId → All projects for a client are in the same partition - Services use /projectId → All services for a project are in the same partition - Enables efficient queries within a client's data - Reduces RU costs for related data access

Self-referencing: - Clients use /clientId (same as id) - Crews use /crewId (same as id) - Simpler partition key management - Efficient single-item lookups

Categorical: - CrewTrades use /tradeCode → Group by trade type - CrewMembers use /locationKey → Group by location/time - Equipment use /equipmentCode → Group by equipment type - Enables efficient category-based queries


API Router Structure

Router Organization

app/api/v1/routers/
├─ clients.py
│  ├─ GET    /api/v1/clients
│  ├─ POST   /api/v1/clients
│  ├─ GET    /api/v1/clients/{client_id}
│  ├─ PUT    /api/v1/clients/{client_id}
│  └─ DELETE /api/v1/clients/{client_id}
├─ projects.py
│  ├─ GET    /api/v1/projects
│  ├─ POST   /api/v1/projects
│  ├─ GET    /api/v1/projects/{project_id}
│  ├─ PUT    /api/v1/projects/{project_id}
│  └─ DELETE /api/v1/projects/{project_id}
├─ services.py
│  ├─ GET    /api/v1/projects/{project_id}/services
│  ├─ POST   /api/v1/projects/{project_id}/services
│  ├─ GET    /api/v1/projects/{project_id}/services/{service_id}
│  ├─ PUT    /api/v1/projects/{project_id}/services/{service_id}
│  └─ DELETE /api/v1/projects/{project_id}/services/{service_id}
├─ crews.py
│  ├─ Crews:
│  │  ├─ GET    /api/v1/crews
│  │  ├─ POST   /api/v1/crews
│  │  ├─ GET    /api/v1/crews/{crew_id}
│  │  ├─ PUT    /api/v1/crews/{crew_id}
│  │  ├─ DELETE /api/v1/crews/{crew_id}
│  │  └─ POST   /api/v1/crews/{crew_id}/duplicate
│  │
│  ├─ Crew Trades:
│  │  ├─ GET    /api/v1/crews/trades
│  │  ├─ POST   /api/v1/crews/trades
│  │  └─ GET    /api/v1/crews/trades/{trade_code}
│  │
│  └─ Crew Members:
│     ├─ GET    /api/v1/crews/members
│     ├─ POST   /api/v1/crews/members
│     └─ GET    /api/v1/crews/members/{member_id}
├─ service_crews.py
│  ├─ GET    /api/v1/service-crews
│  ├─ POST   /api/v1/service-crews
│  ├─ GET    /api/v1/service-crews/{service_crew_id}
│  ├─ PUT    /api/v1/service-crews/{service_crew_id}
│  └─ DELETE /api/v1/service-crews/{service_crew_id}
└─ equipment.py
   ├─ GET    /api/v1/equipment
   ├─ POST   /api/v1/equipment
   ├─ GET    /api/v1/equipment/{equipment_id}
   ├─ PUT    /api/v1/equipment/{equipment_id}
   └─ DELETE /api/v1/equipment/{equipment_id}

Error Handling Flow

How Errors Propagate

Error occurs in Repository
┌─────────────────────────────┐
│ try:                        │
│   result = await container  │
│     .create_item(...)       │
│ except CosmosHttpResponse   │
│   Error as e:               │
│   logger.error(...)         │
│   raise  ← Re-raise         │
└────────┬────────────────────┘
Error bubbles to Router
┌─────────────────────────────┐
│ try:                        │
│   client = await repo       │
│     .create_client(...)     │
│ except ValueError as e:     │
│   raise HTTPException(      │
│     status_code=400,        │
│     detail=str(e)           │
│   )                         │
│ except Exception as e:      │
│   logger.error(...)         │
│   raise HTTPException(      │
│     status_code=500,        │
│     detail="Internal error" │
│   )                         │
└────────┬────────────────────┘
FastAPI catches HTTPException
┌─────────────────────────────┐
│ Returns HTTP Response:      │
│ {                           │
│   "detail": "Error message" │
│ }                           │
│ Status: 400/404/500/etc.    │
└─────────────────────────────┘

Lifecycle Management

Application Startup and Shutdown

Application Start
┌──────────────────────────────┐
│ main.py loads                │
└──────────┬───────────────────┘
┌──────────────────────────────┐
│ @asynccontextmanager         │
│ async def lifespan(app):     │
└──────────┬───────────────────┘
           ↓ STARTUP
┌──────────────────────────────┐
│ await cosmos_db.initialize() │
│ • Connect to Cosmos DB       │
│ • Get/create database        │
│ • Get/create containers      │
│ • Set _initialized = True    │
└──────────┬───────────────────┘
┌──────────────────────────────┐
│ Application Running          │
│ • Accepting requests         │
│ • Processing endpoints       │
│ • Using database             │
└──────────┬───────────────────┘
           ↓ SHUTDOWN SIGNAL
┌──────────────────────────────┐
│ await cosmos_db.close()      │
│ • Close Cosmos client        │
│ • Set _initialized = False   │
└──────────┬───────────────────┘
    Application Stopped

Data Transformation Flow

Pydantic Model Transformations

Frontend sends JSON
┌─────────────────────────────┐
│ {                           │
│   "clientCode": "ACME",     │
│   "clientName": "ACME Corp" │
│ }                           │
└────────┬────────────────────┘
         ↓ FastAPI deserializes
┌─────────────────────────────┐
│ ClientCreate(               │
│   client_code="ACME",       │  ← Field aliases
│   client_name="ACME Corp"   │     convert camelCase
│ )                           │     to snake_case
└────────┬────────────────────┘
         ↓ Repository transforms
┌─────────────────────────────┐
│ client_dict = data          │
│   .model_dump(              │
│     by_alias=True,          │  ← Use aliases
│     mode='json'             │     for output
│   )                         │
│ # Result:                   │
│ # {                         │
│ #   "clientCode": "ACME",   │
│ #   "clientName": "ACME..."│
│ # }                         │
└────────┬────────────────────┘
         ↓ Add metadata
┌─────────────────────────────┐
│ client_dict['id'] = uuid()  │
│ client_dict['clientId'] =   │
│   client_dict['id']         │
│ client_dict['type'] =       │
│   'client'                  │
│ client_dict['createdAt'] =  │
│   datetime.utcnow()...      │
└────────┬────────────────────┘
         ↓ Save to Cosmos DB
┌─────────────────────────────┐
│ await container             │
│   .create_item(client_dict) │
└────────┬────────────────────┘
         ↓ Cosmos returns dict
┌─────────────────────────────┐
│ {                           │
│   "id": "uuid-123",         │
│   "clientCode": "ACME",     │
│   "clientName": "ACME Corp",│
│   "clientId": "uuid-123",   │
│   "type": "client",         │
│   "createdAt": "2025-...",  │
│   "_rid": "...",            │  ← Cosmos metadata
│   "_self": "..."            │
│ }                           │
└────────┬────────────────────┘
         ↓ Convert to Pydantic
┌─────────────────────────────┐
│ Client(**result)            │
│ • Validates                 │
│ • Filters Cosmos metadata   │
│ • Creates typed object      │
└────────┬────────────────────┘
         ↓ FastAPI serializes
┌─────────────────────────────┐
│ {                           │
│   "id": "uuid-123",         │
│   "clientCode": "ACME",     │  ← camelCase
│   "clientName": "ACME Corp",│     for frontend
│   "clientId": "uuid-123",   │
│   "createdAt": "2025-..."   │
│ }                           │
└─────────────────────────────┘
   Sent to Frontend

These diagrams provide a visual reference for understanding the backend architecture, data flow, and key patterns used throughout the application.