Arches implements Hexagonal Architecture (Ports & Adapters) with Domain-Driven Design
principles, ensuring separation of concerns, testability, and business logic independence from
infrastructure.
Core Principles
Hexagonal Architecture
Core Domain: Business logic and rules
Ports: Interfaces defining external interactions
Adapters: Implementations connecting to external systems
Dependency Rule
Dependencies flow inward toward the domain core:
Code
External World → Adapters → Ports → Domain Core
Domain Isolation
Each bounded context (auth, organizations, workflows, content) operates independently with:
Own entities and business rules
Dedicated database tables
Separate API endpoints
No cross-domain imports
System Architecture
High-Level Components
Code
graph TB subgraph "Client Layer" WEB[React SPA] CLI[CLI Tools] SDK[TypeScript SDK] end subgraph "API Layer" GATEWAY[API Gateway/Load Balancer] API[REST API Server] DOCS[OpenAPI Docs] end subgraph "Business Layer" AUTH[Auth Domain] ORG[Organizations Domain] WORK[Workflows Domain] CONT[Content Domain] end subgraph "Data Layer" PG[(PostgreSQL + pgvector)] REDIS[(Redis Cache)] S3[Object Storage] end subgraph "External Services" OAUTH[OAuth Providers] AI[AI/ML Services] EMAIL[Email Service] end WEB --> GATEWAY CLI --> GATEWAY SDK --> GATEWAY GATEWAY --> API API --> DOCS API --> AUTH API --> ORG API --> WORK API --> CONT AUTH --> PG AUTH --> REDIS ORG --> PG WORK --> PG WORK --> S3 CONT --> PG CONT --> S3 AUTH --> OAUTH AUTH --> EMAIL WORK --> AI
Request Flow
Code
sequenceDiagram participant Client participant Handler participant Middleware participant Service participant Repository participant Database Client->>Handler: HTTP Request Handler->>Middleware: Authentication Middleware->>Handler: User Context Handler->>Service: Business Operation Service->>Repository: Data Operation Repository->>Database: SQL Query Database->>Repository: Result Repository->>Service: Domain Entity Service->>Handler: Response Handler->>Client: HTTP Response
Domain Architecture
Flat Package Structure
Each domain follows a flat package structure for simplicity:
sequenceDiagram participant User participant API participant AuthMiddleware participant AuthService participant Database participant JWT User->>API: POST /auth/login API->>AuthService: Authenticate(email, password) AuthService->>Database: GetUserByEmail Database->>AuthService: User AuthService->>AuthService: VerifyPassword AuthService->>JWT: GenerateTokens JWT->>AuthService: AccessToken, RefreshToken AuthService->>Database: CreateSession AuthService->>API: Tokens API->>User: 200 OK + Tokens User->>API: GET /api/resource + Bearer Token API->>AuthMiddleware: ValidateToken AuthMiddleware->>JWT: VerifyToken JWT->>AuthMiddleware: Claims AuthMiddleware->>Database: GetSession Database->>AuthMiddleware: Session AuthMiddleware->>API: User Context API->>User: 200 OK + Resource
Database Architecture
Schema Design
Code
-- Multi-tenant foundationCREATE TABLE organizations ( id UUID PRIMARY KEY, name VARCHAR(255) NOT NULL, plan VARCHAR(50) DEFAULT 'free', credits INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);-- User managementCREATE TABLE users ( id UUID PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL, name VARCHAR(255) NOT NULL, password_hash VARCHAR(255), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);-- Organization membershipCREATE TABLE members ( id UUID PRIMARY KEY, user_id UUID REFERENCES users (id), organization_id UUID REFERENCES organizations (id), role VARCHAR(50) NOT NULL, UNIQUE (user_id, organization_id));-- Session managementCREATE TABLE sessions ( token VARCHAR(255) PRIMARY KEY, user_id UUID REFERENCES users (id), expires_at TIMESTAMP NOT NULL, active_organization_id UUID REFERENCES organizations (id));-- Content storage with vectorsCREATE TABLE artifacts ( id UUID PRIMARY KEY, organization_id UUID REFERENCES organizations (id), name VARCHAR(255) NOT NULL, content TEXT, embedding vector (1536), -- pgvector for similarity search metadata JSONB, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);-- Workflow definitionsCREATE TABLE pipelines ( id UUID PRIMARY KEY, organization_id UUID REFERENCES organizations (id), name VARCHAR(255) NOT NULL, definition JSONB NOT NULL, -- DAG structure created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);-- Workflow executionsCREATE TABLE runs ( id UUID PRIMARY KEY, pipeline_id UUID REFERENCES pipelines (id), status VARCHAR(50) NOT NULL, progress DECIMAL(5, 2) DEFAULT 0, started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, completed_at TIMESTAMP);
Indexing Strategy
Code
-- Performance indexesCREATE INDEX idx_users_email ON users (email);CREATE INDEX idx_sessions_token ON sessions (token);CREATE INDEX idx_sessions_user_id ON sessions (user_id);CREATE INDEX idx_members_user_org ON members (user_id, organization_id);CREATE INDEX idx_artifacts_org ON artifacts (organization_id);CREATE INDEX idx_pipelines_org ON pipelines (organization_id);CREATE INDEX idx_runs_pipeline ON runs (pipeline_id);CREATE INDEX idx_runs_status ON runs (status);-- Vector similarity searchCREATE INDEX idx_artifacts_embedding ON artifacts USING ivfflat (embedding vector_cosine_ops)WITH (lists = 100);
Caching Architecture
Cache Layers
Application Cache (Redis)
Session data
User profiles
Organization metadata
Temporary computation results
Database Cache (PostgreSQL)
Query result caching
Prepared statement caching
Connection pooling
CDN Cache (CloudFlare/CloudFront)
Static assets
API responses for public data
Cache Patterns
Code
// Cache-Aside Patternfunc (s *Service) GetUser(ctx context.Context, id uuid.UUID) (*User, error) { // Try cache first user, err := s.cache.GetUser(ctx, id) if err == nil { return user, nil } // Cache miss - get from database user, err = s.repo.GetUserByID(ctx, id) if err != nil { return nil, err } // Update cache for next time _ = s.cache.SetUser(ctx, user, 5*time.Minute) return user, nil}// Write-Through Patternfunc (s *Service) UpdateUser(ctx context.Context, user *User) error { // Update database if err := s.repo.UpdateUser(ctx, user); err != nil { return err } // Update cache _ = s.cache.SetUser(ctx, user, 5*time.Minute) // Publish update event _ = s.events.PublishUserUpdated(ctx, user) return nil}
// Process in batches to avoid memory issuesconst batchSize = 100for i := 0; i < len(items); i += batchSize { end := i + batchSize if end > len(items) { end = len(items) } processBatch(items[i:end])}
Concurrent Processing
Code
// Use worker pool patternjobs := make(chan Job, 100)results := make(chan Result, 100)// Start workersfor w := 1; w <= numWorkers; w++ { go worker(jobs, results)}// Send jobsfor _, job := range allJobs { jobs <- job}close(jobs)