# Data Model: Base Architecture - Foundation Layers

**Feature**: 004-base-architecture
**Date**: 2026-06-04

## Entity Overview

| Entity | Location | Purpose |
|--------|----------|---------|
| User | app/Models/User.php | Unified human entity (customer/agent/investor inferred from relationships). Hard immutable - cannot be deleted. |
| Admin | app/Domains/Auth/Models/Admin.php | System administrator with Spatie RBAC using 'admin' guard |
| Contract | app/Domains/Contract/Models/Contract.php | Installment agreement - immutable after signing |
| Installment | app/Domains/Contract/Models/Installment.php | State table for due dates and payment status |
| Payment | app/Domains/Payment/Models/Payment.php | Financial ledger entry with LIFO modification |
| PaymentAuditLog | app/Domains/Payment/Models/PaymentAuditLog.php | Append-only narrative log for payment changes |
| AgentSharesLog | app/Domains/Agent/Models/AgentSharesLog.php | Append-only transaction log for agent share movements |
| NotificationTemplate | app/Domains/Notification/Models/NotificationTemplate.php | Standalone template for dynamic notification generation |

---

## User Model

**File**: `app/Models/User.php`

**Purpose**: Unified human entity - cannot be deleted (hard immutability). Has customer/agent/investor roles inferred from relationships.

**Table**: `users` (assumed from SP-02)

**Attributes**:
| Field | Type | Notes |
|-------|------|-------|
| id | BIGSERIAL | Primary key |
| name | VARCHAR(255) | |
| email | VARCHAR(255) | Unique |
| phone | VARCHAR(20) | Optional |
| image | VARCHAR(255) | Optional |
| description | TEXT | Optional |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |

**Relationships**:
| Method | Type | Target | Notes |
|--------|------|--------|-------|
| contractsAsCustomer() | HasMany | Contract | Where customer_id = user.id |
| contractsAsAgent() | HasMany | Contract | Where agent_id = user.id |
| sharesLogs() | HasMany | AgentSharesLog | Where agent_id = user.id |

**Casting**:
- All monetary fields: `decimal(10,2)` (if any exist on User)

**Hard Immutability**:
```php
public function delete(): void
{
    throw new RuntimeException('Users cannot be deleted');
}

public function destroy($ids): void
{
    throw new RuntimeException('Users cannot be deleted');
}
```

---

## Admin Model

**File**: `app/Domains/Auth/Models/Admin.php`

**Purpose**: System administrator with Spatie RBAC integration using 'admin' guard.

**Table**: `admins` (assumed from SP-02)

**Attributes**:
| Field | Type | Notes |
|-------|------|-------|
| id | BIGSERIAL | Primary key |
| name | VARCHAR(255) | |
| email | VARCHAR(255) | Unique |
| password | VARCHAR(255) | Hashed |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |

**Traits**: `HasRoles` (from spatie/laravel-permission)

**Guard**: `guard_name = 'admin'`

---

## Contract Model

**File**: `app/Domains/Contract/Models/Contract.php`

**Purpose**: Installment agreement - immutable after signing.

**Table**: `contracts` (assumed from SP-02)

**Attributes**:
| Field | Type | Notes |
|-------|------|-------|
| id | BIGSERIAL | Primary key |
| customer_id | BIGINT | FK to users |
| agent_id | BIGINT | FK to users |
| purchase_amount | DECIMAL(10,2) | Monetary |
| initial_payment | DECIMAL(10,2) | Must be less than purchase_amount |
| num_installments | INT | 1-60 range |
| status | VARCHAR(50) | Contract status |
| signed_at | TIMESTAMP | Nullable |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |

**Relationships**:
| Method | Type | Target | Notes |
|--------|------|--------|-------|
| customer() | BelongsTo | User | Via customer_id |
| agent() | BelongsTo | User | Via agent_id |
| installments() | HasMany | Installment | Ordered by installment_number ASC |
| payments() | HasMany | Payment | |
| auditLogs() | HasMany | PaymentAuditLog | Ordered by created_at DESC |

**Casting**:
- purchase_amount: `decimal(10,2)`
- initial_payment: `decimal(10,2)`

---

## Installment Model

**File**: `app/Domains/Contract/Models/Installment.php`

**Purpose**: State table for due dates and payment status tracking.

**Table**: `installments` (assumed from SP-02)

**Attributes**:
| Field | Type | Notes |
|-------|------|-------|
| id | BIGSERIAL | Primary key |
| contract_id | BIGINT | FK to contracts |
| installment_number | INT | Order sequence |
| due_date | DATE | |
| amount | DECIMAL(10,2) | Monetary |
| status | VARCHAR(50) | pending/paid/overdue |
| paid_at | TIMESTAMP | Nullable |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |

**Relationships**:
| Method | Type | Target | Notes |
|--------|------|--------|-------|
| contract() | BelongsTo | Contract | |

**Casting**:
- amount: `decimal(10,2)`

---

## Payment Model

**File**: `app/Domains/Payment/Models/Payment.php`

**Purpose**: Financial ledger entry with LIFO modification capability.

**Table**: `payments` (assumed from SP-02)

**Attributes**:
| Field | Type | Notes |
|-------|------|-------|
| id | BIGSERIAL | Primary key |
| contract_id | BIGINT | FK to contracts |
| installment_id | BIGINT | FK to installments |
| amount | DECIMAL(10,2) | Monetary |
| payment_date | DATE | For analytics filtering |
| notes | TEXT | Optional |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |

**Relationships**:
| Method | Type | Target | Notes |
|--------|------|--------|-------|
| contract() | BelongsTo | Contract | |
| auditLogs() | HasMany | PaymentAuditLog | Ordered by created_at DESC |

**Casting**:
- amount: `decimal(10,2)`

---

## PaymentAuditLog Model

**File**: `app/Domains/Payment/Models/PaymentAuditLog.php`

**Purpose**: Append-only narrative log for payment changes.

**Table**: `payment_audit_logs` (assumed from SP-02)

**Attributes**:
| Field | Type | Notes |
|-------|------|-------|
| id | BIGSERIAL | Primary key |
| contract_id | BIGINT | FK to contracts |
| payment_id | BIGINT | FK to payments (nullable) |
| action | VARCHAR(50) | created/updated/deleted |
| old_values | JSON | Previous state |
| new_values | JSON | New state |
| description | TEXT | Narrative description |
| created_at | TIMESTAMP | |

**Relationships**:
| Method | Type | Target | Notes |
|--------|------|--------|-------|
| contract() | BelongsTo | Contract | |
| payment() | BelongsTo | Payment | Nullable |

---

## AgentSharesLog Model

**File**: `app/Domains/Agent/Models/AgentSharesLog.php`

**Purpose**: Append-only transaction log for agent share movements.

**Table**: `agent_shares_logs` (assumed from SP-02)

**Attributes**:
| Field | Type | Notes |
|-------|------|-------|
| id | BIGSERIAL | Primary key |
| agent_id | BIGINT | FK to users |
| action | VARCHAR(20) | add/withdraw |
| amount | DECIMAL(10,2) | Monetary (200 USD fixed value) |
| notes | TEXT | Optional |
| transaction_date | DATE | |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |

**Relationships**:
| Method | Type | Target | Notes |
|--------|------|--------|-------|
| agent() | BelongsTo | User | |

**Casting**:
- amount: `decimal(10,2)`

---

## NotificationTemplate Model

**File**: `app/Domains/Notification/Models/NotificationTemplate.php`

**Purpose**: Standalone template for dynamic notification generation.

**Table**: `notification_templates` (assumed from SP-02)

**Attributes**:
| Field | Type | Notes |
|-------|------|-------|
| id | BIGSERIAL | Primary key |
| name | VARCHAR(255) | Template name |
| type | VARCHAR(50) | sms/email/push |
| subject | VARCHAR(255) | Optional |
| body | TEXT | Template body with placeholders |
| variables | JSON | Available placeholder variables |
| is_active | BOOLEAN | Default true |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |

**Relationships**: None (standalone model per spec FR-008)

---

## Infrastructure Classes

### BaseRepositoryInterface

**File**: `app/Shared/Repositories/Contracts/BaseRepositoryInterface.php`

**Methods**:
| Method | Signature | Return Type |
|--------|-----------|-------------|
| all | (array $columns = ['*']) | Collection |
| find | (int $id) | ?Model |
| findOrFail | (int $id) | Model |
| create | (array $data) | Model |
| update | (int $id, array $data) | Model |
| delete | (int $id) | bool |
| paginate | (int $perPage = 20) | LengthAwarePaginator |

---

### BaseEloquentRepository

**File**: `app/Shared/Repositories/Eloquent/BaseEloquentRepository.php`

**Purpose**: Implements BaseRepositoryInterface using Laravel's Eloquent ORM.

**Pattern**: All domain repositories extend this class.

---

### BaseResource

**File**: `app/Shared/Http/Resources/BaseResource.php`

**Purpose**: Base class for all API resources ensuring unified response format.

**Response Structure**:
```json
{
  "success": true,
  "message": "Operation successful",
  "data": { ... }
}
```

---

## Data Model Diagram

```
User (Unified Human Entity)
├── contractsAsCustomer() → Contract (customer_id)
├── contractsAsAgent() → Contract (agent_id)
└── sharesLogs() → AgentSharesLog

Admin (RBAC Entity)
└── HasRoles trait (spatie)

Contract (Installment Agreement)
├── belongsTo → User (customer)
├── belongsTo → User (agent)
├── hasMany → Installment (ordered by installment_number)
├── hasMany → Payment
└── hasMany → PaymentAuditLog (ordered by created_at DESC)

Installment (State Table)
└── belongsTo → Contract

Payment (Ledger Entry)
├── belongsTo → Contract
└── hasMany → PaymentAuditLog

PaymentAuditLog (Append-only Narrative)
├── belongsTo → Contract
└── belongsTo → Payment (nullable)

AgentSharesLog (Append-only Transaction)
└── belongsTo → User (agent)

NotificationTemplate (Standalone)
└── No relationships
```

---

## Validation Rules Summary

| Entity | Rule |
|--------|------|
| User | Cannot be deleted (hard immutability) |
| Contract.initial_payment | Must be less than purchase_amount |
| Contract.num_installments | 1-60 range |
| AgentSharesLog.amount | Fixed 200 USD per share value |
| All monetary fields | DECIMAL(10,2) casting |
| User delete() | Throws RuntimeException |

---

## Migration Dependencies

All migrations assumed to exist from SP-02:
- `users` table
- `admins` table
- `contracts` table
- `installments` table
- `payments` table
- `payment_audit_logs` table
- `agent_shares_logs` table
- `notification_templates` table
- `roles` table (spatie)
- `permissions` table (spatie)
- `role_permissions` table (spatie)
- `model_has_roles` table (spatie)
- `model_has_permissions` table (spatie)
- `jobs` table (Laravel default)
- `failed_jobs` table (Laravel default)