Skip to main content

ADR-006-v4: Data Model Architecture - Part 2 (Technical)

Table of Contents​

↑ Back to Top

Document Specification Block​

Document: ADR-006-v4-data-model-part2-technical
Version: 1.0.1
Purpose: Provide exact technical specifications for CODITECT's multi-tenant data models
Audience: AI agents, developers implementing the data layer
Date Created: 2025-08-31
Date Modified: 2025-08-31
QA Review Date: 2025-08-31
Status: DRAFT
User Story: As a developer, I need a comprehensive data model that ensures complete tenant isolation while supporting human-AI collaboration

1. Technical Requirements​

1.1 Constraints​

  • MUST prefix all keys with tenant_id
  • MUST NOT allow cross-tenant queries
  • MUST use transactions for related updates
  • MUST validate all UUIDs before storage
  • MUST maintain audit trail for all mutations

1.2 Dependencies​

[dependencies]
foundationdb = "0.8"
uuid = { version = "1.6", features = ["v4", "serde"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
chrono = { version = "0.4", features = ["serde"] }
argon2 = "0.5"
anyhow = "1.0"
thiserror = "1.0"
# CODI logging integration
coditect-logging = { path = "../logging" }

↑ Back to Top

2. Core Models Implementation​

2.1 User Model​

// src/models/user.rs
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::logging::{CoditecLogger, LogLevel, LogEntry};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct User {
pub user_id: Uuid,
pub tenant_id: Uuid,
pub email: String,
pub first_name: String,
pub last_name: String,
pub company: Option<String>,
pub role: UserRole,
pub is_active: bool,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub last_login: Option<DateTime<Utc>>,
pub metadata: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub password_hash: Option<String>, // Argon2id hash
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum UserRole {
Admin, // Full tenant management
Developer, // Create and manage resources
Manager, // Team and project oversight
Viewer, // Read-only access
}

// Key patterns for User
pub struct UserKeys;

impl UserKeys {
// Primary key
pub fn user_key(tenant_id: &Uuid, user_id: &Uuid) -> String {
format!("{}/users/{}", tenant_id, user_id)
}

// Secondary index for email lookup
pub fn email_index_key(tenant_id: &Uuid, email: &str) -> String {
format!("{}/user_by_email/{}", tenant_id, email.to_lowercase())
}

// Range query for all users in tenant
pub fn user_range_prefix(tenant_id: &Uuid) -> String {
format!("{}/users/", tenant_id)
}
}

// JWT/Auth Integration
impl User {
pub fn to_jwt_claims(&self) -> JwtClaims {
JwtClaims {
sub: self.user_id.to_string(),
tenant_id: self.tenant_id.to_string(),
email: self.email.clone(),
role: self.role.to_string(),
exp: (Utc::now() + Duration::hours(24)).timestamp(),
}
}

pub fn validate_password(&self, password: &str) -> Result<bool> {
match &self.password_hash {
Some(hash) => {
let parsed = PasswordHash::new(hash)?;
Ok(Argon2::default().verify_password(password.as_bytes(), &parsed).is_ok())
}
None => Ok(false),
}
}
}

2.2 Member Model (Unified Human/AI Abstraction)​

// src/models/member.rs
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Member {
pub member_id: Uuid,
pub tenant_id: Uuid,
pub user_id: Option<Uuid>, // Link to User if human
pub member_type: MemberType,
pub name: String,
pub email: Option<String>, // For humans
pub avatar_url: Option<String>,
pub status: MemberStatus,
pub capabilities: Vec<String>, // Skills/capabilities
pub availability: Availability,
pub capacity: Capacity,
pub metadata: Option<serde_json::Value>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum MemberType {
Human,
Agent,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Capacity {
pub max_concurrent_tasks: i32,
pub current_task_count: i32,
pub weekly_hours: Option<f32>, // For humans
pub tokens_per_hour: Option<i32>, // For agents
}

// Key patterns for Member
pub struct MemberKeys;

impl MemberKeys {
pub fn member_key(tenant_id: &Uuid, member_id: &Uuid) -> String {
format!("{}/members/{}", tenant_id, member_id)
}

pub fn members_by_type(tenant_id: &Uuid, member_type: &MemberType) -> String {
format!("{}/members_by_type/{}/", tenant_id, member_type)
}

pub fn available_members(tenant_id: &Uuid) -> String {
format!("{}/available_members/", tenant_id)
}
}

2.3 Entity Model (Organizational Hierarchy)​

// src/models/entity.rs
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Entity {
pub entity_id: Uuid,
pub tenant_id: Uuid,
pub parent_entity_id: Option<Uuid>,
pub entity_type: EntityType,
pub name: String,
pub code: String,
pub description: String,
pub hierarchy_level: HierarchyLevel,
pub is_active: bool,
pub metadata: Option<serde_json::Value>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum HierarchyLevel {
Corporation,
Division,
Department,
Team,
}

// Key patterns for Entity
pub struct EntityKeys;

impl EntityKeys {
pub fn entity_key(tenant_id: &Uuid, entity_id: &Uuid) -> String {
format!("{}/entities/{}", tenant_id, entity_id)
}

pub fn children_key(tenant_id: &Uuid, parent_id: &Uuid) -> String {
format!("{}/entity_children/{}/", tenant_id, parent_id)
}

pub fn entities_by_level(tenant_id: &Uuid, level: &HierarchyLevel) -> String {
format!("{}/entities_by_level/{}/", tenant_id, level)
}
}

↑ Back to Top

3. Project Management Models​

3.1 Project Model​

// src/models/project.rs
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Project {
pub project_id: Uuid,
pub tenant_id: Uuid,
pub entity_id: Uuid,
pub subsidiary_id: Uuid,
pub name: String,
pub code: String,
pub description: String,
pub status: ProjectStatus,
pub priority: Priority,
pub project_type: ProjectType,
pub manager_id: Uuid,
pub sponsor_id: Option<Uuid>,
pub team_ids: Vec<Uuid>,
pub timeline: ProjectTimeline,
pub budget: Budget,
pub technology_stack: Vec<String>,
pub repository_ids: Vec<Uuid>,
pub compliance_requirements: Vec<String>,
pub risk_tolerance: RiskTolerance,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub created_by: Uuid,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProjectTimeline {
pub planned_start: DateTime<Utc>,
pub planned_end: DateTime<Utc>,
pub actual_start: Option<DateTime<Utc>>,
pub actual_end: Option<DateTime<Utc>>,
pub milestones: Vec<Milestone>,
}

// Key patterns for Project
pub struct ProjectKeys;

impl ProjectKeys {
pub fn project_key(tenant_id: &Uuid, project_id: &Uuid) -> String {
format!("{}/projects/{}", tenant_id, project_id)
}

pub fn projects_by_entity(tenant_id: &Uuid, entity_id: &Uuid) -> String {
format!("{}/projects_by_entity/{}/", tenant_id, entity_id)
}

pub fn projects_by_status(tenant_id: &Uuid, status: &ProjectStatus) -> String {
format!("{}/projects_by_status/{}/", tenant_id, status)
}
}

3.2 Task Model​

// src/models/task.rs
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Task {
pub task_id: Uuid,
pub tenant_id: Uuid,
pub project_id: Uuid,
pub parent_task_id: Option<Uuid>,
pub title: String,
pub description: String,
pub status: TaskStatus,
pub priority: TaskPriority,
pub task_type: TaskType,
pub assigned_to: Option<Uuid>, // Member ID
pub estimated_hours: Option<f32>,
pub actual_hours: Option<f32>,
pub due_date: Option<DateTime<Utc>>,
pub completed_at: Option<DateTime<Utc>>,
pub dependencies: Vec<Uuid>,
pub tags: Vec<String>,
pub attachments: Vec<Attachment>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub created_by: Uuid,
}

// Key patterns for Task
pub struct TaskKeys;

impl TaskKeys {
pub fn task_key(tenant_id: &Uuid, task_id: &Uuid) -> String {
format!("{}/tasks/{}", tenant_id, task_id)
}

pub fn tasks_by_project(tenant_id: &Uuid, project_id: &Uuid) -> String {
format!("{}/tasks_by_project/{}/", tenant_id, project_id)
}

pub fn tasks_by_assignee(tenant_id: &Uuid, member_id: &Uuid) -> String {
format!("{}/tasks_by_assignee/{}/", tenant_id, member_id)
}
}

↑ Back to Top

4. AI and Automation Models​

4.1 Agent Configuration Model​

// src/models/agent_config.rs
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentConfig {
pub config_id: Uuid,
pub tenant_id: Uuid,
pub name: String,
pub agent_type: AgentType,
pub description: String,
pub capabilities: Vec<AgentCapability>,
pub provider: AIProvider,
pub model: String,
pub max_tokens: i32,
pub temperature: f32,
pub tools: Vec<ToolConfig>,
pub cost_per_token: f64,
pub is_active: bool,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AgentType {
Coder,
Reviewer,
Tester,
Documenter,
Orchestrator,
}

// Key patterns for AgentConfig
pub struct AgentConfigKeys;

impl AgentConfigKeys {
pub fn config_key(tenant_id: &Uuid, config_id: &Uuid) -> String {
format!("{}/agent_configs/{}", tenant_id, config_id)
}

pub fn configs_by_type(tenant_id: &Uuid, agent_type: &AgentType) -> String {
format!("{}/agent_configs_by_type/{}/", tenant_id, agent_type)
}
}

4.2 Workflow Model​

// src/models/workflow.rs
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Workflow {
pub workflow_id: Uuid,
pub tenant_id: Uuid,
pub name: String,
pub description: String,
pub trigger_type: TriggerType,
pub steps: Vec<WorkflowStep>,
pub error_handling: ErrorHandling,
pub timeout_seconds: i32,
pub max_retries: i32,
pub is_active: bool,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkflowStep {
pub step_id: Uuid,
pub name: String,
pub action_type: ActionType,
pub agent_config_id: Option<Uuid>,
pub inputs: serde_json::Value,
pub outputs: Vec<String>,
pub conditions: Vec<StepCondition>,
pub timeout_seconds: i32,
}

↑ Back to Top

5. Access Control and Compliance​

5.1 RBAC Model​

// src/models/rbac.rs
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Role {
pub role_id: Uuid,
pub tenant_id: Uuid,
pub name: String,
pub description: String,
pub permissions: Vec<Permission>,
pub is_system: bool,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Permission {
pub resource: ResourceType,
pub actions: Vec<Action>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ResourceType {
User,
Project,
Task,
Agent,
Workflow,
Audit,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Action {
Create,
Read,
Update,
Delete,
Execute,
}

5.2 Audit Model​

// src/models/audit_event.rs
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditEvent {
pub event_id: Uuid,
pub tenant_id: Uuid,
pub timestamp: DateTime<Utc>,
pub actor: AuditActor,
pub action: String,
pub resource_type: String,
pub resource_id: String,
pub changes: Option<serde_json::Value>,
pub result: AuditResult,
pub ip_address: Option<String>,
pub user_agent: Option<String>,
pub request_id: Option<String>,
pub metadata: Option<serde_json::Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditActor {
pub actor_type: ActorType,
pub actor_id: Uuid,
pub actor_name: String,
}

// Key pattern for append-only audit log
pub struct AuditKeys;

impl AuditKeys {
pub fn audit_key(tenant_id: &Uuid, timestamp: &DateTime<Utc>, event_id: &Uuid) -> String {
format!("{}/audit_log/{}:{}", tenant_id, timestamp.to_rfc3339(), event_id)
}
}

↑ Back to Top

6. Business Operations Models​

6.1 Usage Model​

// src/models/usage.rs
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Usage {
pub usage_id: Uuid,
pub tenant_id: Uuid,
pub user_id: Uuid,
pub resource_type: ResourceType,
pub resource_id: String,
pub action: String,
pub quantity: f64,
pub unit: UsageUnit,
pub cost: f64,
pub timestamp: DateTime<Utc>,
pub provider: Option<String>,
pub metadata: Option<serde_json::Value>,
}

// Key patterns for usage tracking
pub struct UsageKeys;

impl UsageKeys {
pub fn usage_key(tenant_id: &Uuid, date: &str, user_id: &Uuid, usage_id: &Uuid) -> String {
format!("{}/usage/{}/{}/{}", tenant_id, date, user_id, usage_id)
}

pub fn daily_usage(tenant_id: &Uuid, date: &str) -> String {
format!("{}/usage/{}/", tenant_id, date)
}
}

↑ Back to Top

7. Key Patterns and Indexes​

7.1 Primary Key Patterns​

// All primary keys follow this pattern
{tenant_id}/{entity_type}/{entity_id}

// Examples:
"12345/users/67890"
"12345/projects/abcdef"
"12345/tasks/xyz123"

7.2 Secondary Index Patterns​

// Lookup indexes
{tenant_id}/{entity_type}_by_{field}/{field_value} -> entity_id

// Examples:
"12345/user_by_email/john@example.com" -> "67890"
"12345/projects_by_status/active/" -> [project_ids]

7.3 Range Query Patterns​

// List all entities of a type
{tenant_id}/{entity_type}/

// List related entities
{tenant_id}/{parent_type}_{child_type}/{parent_id}/

// Examples:
"12345/users/" -> All users in tenant
"12345/tasks_by_project/proj123/" -> All tasks in project

↑ Back to Top

8. Testing Requirements​

8.1 Multi-Tenant Isolation Tests​

#[cfg(test)]
mod tests {
use super::*;

#[tokio::test]
async fn test_tenant_isolation() {
let db = setup_test_fdb().await;
let tenant1 = Uuid::new_v4();
let tenant2 = Uuid::new_v4();

// Create users in different tenants
let user1 = create_user(&db, tenant1, "user1@tenant1.com").await;
let user2 = create_user(&db, tenant2, "user2@tenant2.com").await;

// Verify cannot access across tenants
let result = get_user(&db, tenant1, user2.user_id).await;
assert!(result.is_err());

// Verify range queries respect boundaries
let users = list_users(&db, tenant1).await.unwrap();
assert_eq!(users.len(), 1);
assert_eq!(users[0].user_id, user1.user_id);
}

#[tokio::test]
async fn test_key_pattern_validation() {
let tenant_id = Uuid::new_v4();
let user_id = Uuid::new_v4();

let key = UserKeys::user_key(&tenant_id, &user_id);
assert!(key.starts_with(&tenant_id.to_string()));
assert!(key.contains("/users/"));
assert!(key.ends_with(&user_id.to_string()));
}
}

↑ Back to Top

9. Security Controls​

9.1 Access Validation​

pub async fn validate_tenant_access(
db: &Database,
tenant_id: &Uuid,
user_id: &Uuid,
) -> Result<()> {
// Verify user belongs to tenant
let user_key = UserKeys::user_key(tenant_id, user_id);
let user_data = db.get(&user_key).await?
.ok_or_else(|| anyhow!("User not found in tenant"))?;

let user: User = serde_json::from_slice(&user_data)?;
if user.tenant_id != *tenant_id {
return Err(anyhow!("Tenant mismatch"));
}

Ok(())
}

9.2 Audit Logging​

pub async fn log_data_access(
db: &Database,
tenant_id: &Uuid,
actor: &AuditActor,
action: &str,
resource: &str,
) -> Result<()> {
let event = AuditEvent {
event_id: Uuid::new_v4(),
tenant_id: *tenant_id,
timestamp: Utc::now(),
actor: actor.clone(),
action: action.to_string(),
resource_type: resource.to_string(),
// ... other fields
};

let key = AuditKeys::audit_key(tenant_id, &event.timestamp, &event.event_id);
db.set(&key, &serde_json::to_vec(&event)?).await?;

Ok(())
}

↑ Back to Top

10. Performance Specifications​

10.1 Latency Requirements​

key_operations:
single_key_read: <1ms (p99)
single_key_write: <5ms (p99)
range_query_100_keys: <10ms (p99)
transaction_5_operations: <10ms (p99)

throughput:
reads_per_second: 1,000,000
writes_per_second: 100,000
concurrent_tenants: 10,000+

10.2 Optimization Strategies​

  • Use range queries for listing operations
  • Batch related updates in transactions
  • Cache frequently accessed data
  • Use secondary indexes for lookups

10.3 Cloud Run Deployment Considerations​

  • Stateless design ensures compatibility with Cloud Run scaling
  • Connection pooling to FoundationDB cluster via VPC connector
  • Cold start optimization through lazy initialization
  • Memory limits considered for batch operations (max 32GB)
  • Request timeout of 60 minutes supports long-running migrations

↑ Back to Top

11. Monitoring & Observability​

11.1 CODI Logging Integration​

use crate::logging::{CoditecLogger, LogLevel, LogEntry};

pub async fn log_model_operation(
component: &str,
action: &str,
tenant_id: &Uuid,
details: serde_json::Value,
) {
CoditecLogger::log(LogEntry {
timestamp: Utc::now(),
level: LogLevel::INFO,
component: format!("models.{}", component),
action: action.to_string(),
user_id: None,
tenant_id: Some(tenant_id.to_string()),
request_id: None,
session_id: None,
result: "success".to_string(),
duration_ms: None,
details: Some(details),
error: None,
}).await;
}

↑ Back to Top

12. Constraints for AI Implementation​

12.1 MUST Requirements​

  1. MUST prefix every key with tenant_id
  2. MUST validate UUID format before storage
  3. MUST use transactions for related updates
  4. MUST log all mutations to audit trail
  5. MUST handle all error cases gracefully

12.2 MUST NOT Requirements​

  1. MUST NOT create keys without tenant prefix
  2. MUST NOT allow cross-tenant queries
  3. MUST NOT store sensitive data unencrypted
  4. MUST NOT skip validation checks
  5. MUST NOT ignore transaction failures

12.3 Test Coverage Requirements​

  • Unit tests for all models - 95% minimum coverage
  • Integration tests for key patterns - 90% minimum coverage
  • Security tests for tenant isolation - 100% critical paths
  • Performance tests for latency targets
  • Audit tests for compliance
  • All tests must validate acceptance criteria from user story

↑ Back to Top

References​

Technical Documentation​

↑ Back to Top

Approval Signatures​

Technical Approval​

RoleNameSignatureDate
AuthorAI System (Claude)_________________2025-08-31
Tech Lead____________________________________________
Data Architect____________________________________________
Security Engineer____________________________________________
Database Admin____________________________________________

Implementation Sign-off​

ComponentOwnerTest CoverageSign-off Date
Core Models_____________________%__________
Project Models_____________________%__________
AI Models_____________________%__________
Access Control_____________________%__________
Business Models_____________________%__________

Review History​

VersionDateChangesReviewer
1.0.02025-08-31Initial draft documenting all 21 modelsAI System
1.0.12025-08-31Added QA date, user story, JWT details, test coverage %, Cloud RunAI System

This technical implementation blueprint provides exact specifications for CODITECT's multi-tenant data model architecture. All code must be implemented as specified with complete test coverage and tenant isolation.