# Vendor Domains - Architecture Diagram ## System Architecture Overview ``` ┌─────────────────────────────────────────────────────────────────┐ │ CLIENT REQUEST │ │ POST /vendors/1/domains │ │ {"domain": "myshop.com"} │ └────────────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ ENDPOINT LAYER │ │ app/api/v1/admin/vendor_domains.py │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ @router.post("/{vendor_id}/domains") │ │ def add_vendor_domain( │ │ vendor_id: int, │ │ domain_data: VendorDomainCreate, ◄───┐ │ │ db: Session, │ │ │ current_admin: User │ │ │ ): │ │ │ domain = vendor_domain_service │ │ │ .add_domain(...) │ │ │ return VendorDomainResponse(...) │ │ │ │ │ └─────────────────────┬───────────────────────┼───────────────────┘ │ │ │ │ ┌────────────▼──────────┐ ┌────────▼─────────┐ │ Pydantic Validation │ │ Authentication │ │ (Auto by FastAPI) │ │ Dependency │ └────────────┬──────────┘ └──────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ SERVICE LAYER │ │ app/services/vendor_domain_service.py │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ class VendorDomainService: │ │ │ │ def add_domain(db, vendor_id, domain_data): │ │ ┌─────────────────────────────────────┐ │ │ │ 1. Verify vendor 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 │ │ │ │ - VendorNotFoundException │ │ │ │ - DomainAlreadyExistsException │ │ │ │ - MaxDomainsReachedException │ │ │ └─────────────────────────────────────┘ │ │ │ └─────────────────────┬───────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ DATABASE LAYER │ │ models/database/vendor_domain.py │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ class VendorDomain(Base): │ │ id: int │ │ vendor_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 /vendors/1/domains │ {"domain": "myshop.com", "is_primary": true} │ ▼ ┌────────────────────────────────────────────┐ │ FastAPI Router │ │ ┌──────────────────────────────────────┐ │ │ │ 1. URL Routing │ │ │ │ 2. Pydantic Validation │ │ │ │ 3. Dependency Injection │ │ │ │ - get_db() │ │ │ │ - get_current_admin_user() │ │ │ └──────────────────────────────────────┘ │ └────────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ Endpoint Function │ │ add_vendor_domain() │ │ │ │ ✓ Receives validated data │ │ ✓ Has DB session │ │ ✓ Has authenticated admin user │ │ ✓ Calls service layer │ │ ✓ Returns response model │ └────────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ Service Layer │ │ vendor_domain_service.add_domain() │ │ │ │ Business Logic: │ │ ┌──────────────────────────────────────┐ │ │ │ Vendor Validation │ │ │ │ ├─ Check vendor exists │ │ │ │ └─ Get vendor 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 VendorDomain object │ │ │ │ │ │ │ │ Database Transaction │ │ │ │ ├─ db.add() │ │ │ │ ├─ db.commit() │ │ │ │ └─ db.refresh() │ │ │ └──────────────────────────────────────┘ │ └────────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ Database │ │ INSERT INTO vendor_domains ... │ └────────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ Return to Endpoint │ │ ← VendorDomain object │ └────────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ Endpoint Response │ │ VendorDomainResponse( │ │ 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 /vendors/1/domains │ {"domain": "existing.com"} │ ▼ ┌────────────────────────────────────────────┐ │ Service Layer │ │ │ │ def add_domain(...): │ │ if self._domain_exists(db, domain): │ │ raise VendorDomainAlready │ │ ExistsException( │ │ domain="existing.com", │ │ existing_vendor_id=2 │ │ ) │ └────────────────┬───────────────────────────┘ │ │ Exception raised │ ▼ ┌────────────────────────────────────────────┐ │ Exception Handler │ │ app/exceptions/handler.py │ │ │ │ @app.exception_handler(WizamartException) │ │ async def custom_exception_handler(...): │ │ return JSONResponse( │ │ status_code=exc.status_code, │ │ content=exc.to_dict() │ │ ) │ └────────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ HTTP Response (409 Conflict) │ │ { │ │ "error_code": "VENDOR_DOMAIN_ │ │ ALREADY_EXISTS", │ │ "message": "Domain 'existing.com' │ │ is already registered", │ │ "status_code": 409, │ │ "details": { │ │ "domain": "existing.com", │ │ "existing_vendor_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 /vendors/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: │ │ _wizamart-verify.myshop.com │ │ Value: abc123..." │ └────────────────────────────────────┘ Step 3: Vendor Adds DNS Record ┌──────────┐ │ Vendor │ Adds TXT record at DNS provider └────┬─────┘ │ ▼ ┌────────────────────────────────────┐ │ DNS Provider (GoDaddy/etc) │ │ _wizamart-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 vendor │ └────────────────────────────────────┘ Result: Domain Active! ┌──────────────┐ │ Customer │ Visits https://myshop.com └──────┬───────┘ │ ▼ ┌────────────────────────────────────┐ │ Middleware detects custom domain │ │ Routes to Vendor 1 │ └────────────────────────────────────┘ ``` ## File Structure Visual ``` project/ │ ├── app/ │ ├── api/ │ │ └── v1/ │ │ └── admin/ │ │ ├── vendors.py ✓ Existing (reference) │ │ └── vendor_domains.py ★ NEW (endpoints) │ │ │ ├── services/ │ │ ├── vendor_service.py ✓ Existing (reference) │ │ └── vendor_domain_service.py ★ NEW (business logic) │ │ │ └── exceptions/ │ ├── __init__.py ✓ UPDATE (add exports) │ ├── base.py ✓ Existing │ ├── auth.py ✓ Existing │ ├── admin.py ✓ Existing │ └── vendor_domain.py ★ NEW (custom exceptions) │ └── models/ ├── schema/ │ ├── vendor.py ✓ Existing │ └── vendor_domain.py ★ NEW (pydantic schemas) │ └── database/ ├── vendor.py ✓ UPDATE (add domains relationship) └── vendor_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