# Store Domains - Architecture Diagram ## System Architecture Overview ``` ┌─────────────────────────────────────────────────────────────────┐ │ CLIENT REQUEST │ │ POST /stores/1/domains │ │ {"domain": "myshop.com"} │ └────────────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ ENDPOINT LAYER │ │ app/api/v1/admin/store_domains.py │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ @router.post("/{store_id}/domains") │ │ def add_store_domain( │ │ store_id: int, │ │ domain_data: StoreDomainCreate, ◄───┐ │ │ db: Session, │ │ │ current_admin: User │ │ │ ): │ │ │ domain = store_domain_service │ │ │ .add_domain(...) │ │ │ return StoreDomainResponse(...) │ │ │ │ │ └─────────────────────┬───────────────────────┼───────────────────┘ │ │ │ │ ┌────────────▼──────────┐ ┌────────▼─────────┐ │ Pydantic Validation │ │ Authentication │ │ (Auto by FastAPI) │ │ Dependency │ └────────────┬──────────┘ └──────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ SERVICE LAYER │ │ app/services/store_domain_service.py │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ class StoreDomainService: │ │ │ │ def add_domain(db, store_id, domain_data): │ │ ┌─────────────────────────────────────┐ │ │ │ 1. Verify store exists │ │ │ │ 2. Check domain limit │ │ │ │ 3. Validate domain format │ │ │ │ 4. Check uniqueness │ │ │ │ 5. Handle primary domain logic │ │ │ │ 6. Create database record │ │ │ │ 7. Generate verification token │ │ │ └─────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────┐ │ │ │ Raises Custom Exceptions │ │ │ │ - StoreNotFoundException │ │ │ │ - DomainAlreadyExistsException │ │ │ │ - MaxDomainsReachedException │ │ │ └─────────────────────────────────────┘ │ │ │ └─────────────────────┬───────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ DATABASE LAYER │ │ models/database/store_domain.py │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ class StoreDomain(Base): │ │ id: int │ │ store_id: int (FK) │ │ domain: str (unique) │ │ is_primary: bool │ │ is_active: bool │ │ is_verified: bool │ │ verification_token: str │ │ ssl_status: str │ │ ... │ │ │ └─────────────────────┬───────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ DATABASE │ │ PostgreSQL / MySQL │ └─────────────────────────────────────────────────────────────────┘ ``` ## Request Flow Diagram ``` ┌──────────┐ │ Client │ └────┬─────┘ │ POST /stores/1/domains │ {"domain": "myshop.com", "is_primary": true} │ ▼ ┌────────────────────────────────────────────┐ │ FastAPI Router │ │ ┌──────────────────────────────────────┐ │ │ │ 1. URL Routing │ │ │ │ 2. Pydantic Validation │ │ │ │ 3. Dependency Injection │ │ │ │ - get_db() │ │ │ │ - get_current_admin_api() │ │ │ └──────────────────────────────────────┘ │ └────────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ Endpoint Function │ │ add_store_domain() │ │ │ │ ✓ Receives validated data │ │ ✓ Has DB session │ │ ✓ Has authenticated admin user │ │ ✓ Calls service layer │ │ ✓ Returns response model │ └────────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ Service Layer │ │ store_domain_service.add_domain() │ │ │ │ Business Logic: │ │ ┌──────────────────────────────────────┐ │ │ │ Store Validation │ │ │ │ ├─ Check store exists │ │ │ │ └─ Get store object │ │ │ │ │ │ │ │ Limit Checking │ │ │ │ ├─ Count existing domains │ │ │ │ └─ Enforce max limit │ │ │ │ │ │ │ │ Domain Validation │ │ │ │ ├─ Normalize format │ │ │ │ ├─ Check reserved subdomains │ │ │ │ └─ Validate regex pattern │ │ │ │ │ │ │ │ Uniqueness Check │ │ │ │ └─ Query existing domains │ │ │ │ │ │ │ │ Primary Domain Logic │ │ │ │ └─ Unset other primary domains │ │ │ │ │ │ │ │ Create Record │ │ │ │ ├─ Generate verification token │ │ │ │ ├─ Set initial status │ │ │ │ └─ Create StoreDomain object │ │ │ │ │ │ │ │ Database Transaction │ │ │ │ ├─ db.add() │ │ │ │ ├─ db.commit() │ │ │ │ └─ db.refresh() │ │ │ └──────────────────────────────────────┘ │ └────────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ Database │ │ INSERT INTO store_domains ... │ └────────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ Return to Endpoint │ │ ← StoreDomain object │ └────────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ Endpoint Response │ │ StoreDomainResponse( │ │ id=1, │ │ domain="myshop.com", │ │ is_verified=False, │ │ verification_token="abc123...", │ │ ... │ │ ) │ └────────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ FastAPI Serialization │ │ Convert to JSON │ └────────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ HTTP Response (201 Created) │ │ { │ │ "id": 1, │ │ "domain": "myshop.com", │ │ "is_verified": false, │ │ "verification_token": "abc123...", │ │ ... │ │ } │ └────────────────┬───────────────────────────┘ │ ▼ ┌──────────┐ │ Client │ └──────────┘ ``` ## Error Handling Flow ``` ┌──────────┐ │ Client │ └────┬─────┘ │ POST /stores/1/domains │ {"domain": "existing.com"} │ ▼ ┌────────────────────────────────────────────┐ │ Service Layer │ │ │ │ def add_domain(...): │ │ if self._domain_exists(db, domain): │ │ raise StoreDomainAlready │ │ ExistsException( │ │ domain="existing.com", │ │ existing_store_id=2 │ │ ) │ └────────────────┬───────────────────────────┘ │ │ Exception raised │ ▼ ┌────────────────────────────────────────────┐ │ Exception Handler │ │ app/exceptions/handler.py │ │ │ │ @app.exception_handler(OrionException) │ │ async def custom_exception_handler(...): │ │ return JSONResponse( │ │ status_code=exc.status_code, │ │ content=exc.to_dict() │ │ ) │ └────────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ HTTP Response (409 Conflict) │ │ { │ │ "error_code": "STORE_DOMAIN_ │ │ ALREADY_EXISTS", │ │ "message": "Domain 'existing.com' │ │ is already registered", │ │ "status_code": 409, │ │ "details": { │ │ "domain": "existing.com", │ │ "existing_store_id": 2 │ │ } │ │ } │ └────────────────┬───────────────────────────┘ │ ▼ ┌──────────┐ │ Client │ └──────────┘ ``` ## Component Interaction Diagram ``` ┌─────────────────┐ │ Endpoints │ ◄─── HTTP Requests from client │ (HTTP Layer) │ ───► HTTP Responses to client └────────┬────────┘ │ Calls │ ▼ ┌─────────────────┐ │ Service │ ◄─── Business logic │ Layer │ ───► Returns domain objects └────────┬────────┘ or raises exceptions │ Uses │ ├──────────────────┐ │ │ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ │ Database │ │ Exceptions │ │ Models │ │ (Custom) │ └─────────────────┘ └─────────────────┘ │ │ │ │ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ │ SQLAlchemy │ │ Exception │ │ ORM │ │ Handler │ └─────────────────┘ └─────────────────┘ │ │ │ │ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ │ Database │ │ JSON Error │ │ (PostgreSQL) │ │ Response │ └─────────────────┘ └─────────────────┘ ``` ## Data Flow for Domain Verification ``` Step 1: Add Domain ┌──────────┐ │ Admin │ POST /stores/1/domains └────┬─────┘ {"domain": "myshop.com"} │ ▼ ┌────────────────────────────────────┐ │ System creates domain record │ │ - domain: "myshop.com" │ │ - is_verified: false │ │ - verification_token: "abc123..." │ └────────────────────────────────────┘ Step 2: Get Instructions ┌──────────┐ │ Admin │ GET /domains/1/verification-instructions └────┬─────┘ │ ▼ ┌────────────────────────────────────┐ │ System returns instructions: │ │ "Add TXT record: │ │ _orion-verify.myshop.com │ │ Value: abc123..." │ └────────────────────────────────────┘ Step 3: Store Adds DNS Record ┌──────────┐ │ Store │ Adds TXT record at DNS provider └────┬─────┘ │ ▼ ┌────────────────────────────────────┐ │ DNS Provider (GoDaddy/etc) │ │ _orion-verify.myshop.com TXT │ │ "abc123..." │ └────────────────────────────────────┘ Step 4: Verify Domain ┌──────────┐ │ Admin │ POST /domains/1/verify └────┬─────┘ │ ▼ ┌────────────────────────────────────┐ │ System: │ │ 1. Queries DNS for TXT record │ │ 2. Checks token matches │ │ 3. Updates domain: │ │ - is_verified: true │ │ - verified_at: now() │ └────────────────────────────────────┘ Step 5: Activate Domain ┌──────────┐ │ Admin │ PUT /domains/1 {"is_active": true} └────┬─────┘ │ ▼ ┌────────────────────────────────────┐ │ System activates domain: │ │ - is_active: true │ │ - Domain now routes to store │ └────────────────────────────────────┘ Result: Domain Active! ┌──────────────┐ │ Customer │ Visits https://myshop.com └──────┬───────┘ │ ▼ ┌────────────────────────────────────┐ │ Middleware detects custom domain │ │ Routes to Store 1 │ └────────────────────────────────────┘ ``` ## File Structure Visual ``` project/ │ ├── app/ │ ├── api/ │ │ └── v1/ │ │ └── admin/ │ │ ├── stores.py ✓ Existing (reference) │ │ └── store_domains.py ★ NEW (endpoints) │ │ │ ├── services/ │ │ ├── store_service.py ✓ Existing (reference) │ │ └── store_domain_service.py ★ NEW (business logic) │ │ │ └── exceptions/ │ ├── __init__.py ✓ UPDATE (add exports) │ ├── base.py ✓ Existing │ ├── auth.py ✓ Existing │ ├── admin.py ✓ Existing │ └── store_domain.py ★ NEW (custom exceptions) │ └── models/ ├── schema/ │ ├── store.py ✓ Existing │ └── store_domain.py ★ NEW (pydantic schemas) │ └── database/ ├── store.py ✓ UPDATE (add domains relationship) └── store_domain.py ✓ Existing (database model) Legend: ★ NEW - Files to create ✓ Existing - Files already exist ✓ UPDATE - Files to modify ``` ## Separation of Concerns Visual ``` ┌─────────────────────────────────────────────────────────────┐ │ ENDPOINT LAYER │ │ - HTTP request/response │ │ - FastAPI decorators │ │ - Dependency injection │ │ - Response models │ │ - Documentation │ │ │ │ ✓ No business logic │ │ ✓ No database operations │ │ ✓ No validation (handled by Pydantic) │ └──────────────────────┬──────────────────────────────────────┘ │ │ Calls │ ┌──────────────────────▼──────────────────────────────────────┐ │ SERVICE LAYER │ │ - Business logic │ │ - Database operations │ │ - Transaction management │ │ - Error handling │ │ - Validation logic │ │ - Logging │ │ │ │ ✓ Reusable methods │ │ ✓ Unit testable │ │ ✓ No HTTP concerns │ └──────────────────────┬──────────────────────────────────────┘ │ │ Uses │ ┌──────────────────────▼──────────────────────────────────────┐ │ DATABASE LAYER │ │ - SQLAlchemy models │ │ - Table definitions │ │ - Relationships │ │ - Database constraints │ │ │ │ ✓ Pure data models │ │ ✓ No business logic │ └─────────────────────────────────────────────────────────────┘ ``` This architecture ensures: - ✅ Clean separation of concerns - ✅ Easy to test each layer - ✅ Reusable business logic - ✅ Maintainable codebase - ✅ Follows SOLID principles