- Add AdminMenuConfig model for per-platform menu customization - Add menu registry for centralized menu configuration - Add my-menu-config and platform-menu-config admin pages - Update sidebar with improved layout and Alpine.js interactions - Add FrontendType enum for admin/vendor menu separation - Document self-contained module patterns in session note Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
9.4 KiB
9.4 KiB
Session Note: Self-Contained Module Architecture
Date: 2026-01-26
Plan Reference: docs/proposals/TEMP.md (now this file)
Previous Session: docs/proposals/SESSION_NOTE_2026-01-25_modular-platform-architecture.md
Summary
Transformed thin module wrappers into fully self-contained modules, using CMS as the pilot. Each self-contained module is an autonomous unit with its own services, models, schemas, templates, exceptions, and locales.
Completed Phases
Phase 1: Foundation
Created infrastructure for self-contained modules:
| File | Purpose |
|---|---|
app/modules/contracts/ |
Protocol definitions for cross-module dependencies |
app/templates_config.py |
Multi-directory template loader for module templates |
app/modules/base.py |
Enhanced ModuleDefinition with self-contained flags |
Git tag: pre-modular-architecture
Phase 2: CMS Pilot (Full Self-Contained Module)
Migrated CMS to be the first fully self-contained module:
| Component | Location | Status |
|---|---|---|
| Services | app/modules/cms/services/content_page_service.py |
✅ |
| Models | app/modules/cms/models/content_page.py |
✅ |
| Schemas | app/modules/cms/schemas/content_page.py |
✅ |
| Exceptions | app/modules/cms/exceptions.py |
✅ |
| Locales | app/modules/cms/locales/{en,fr,de,lb}.json |
✅ |
| Templates | app/modules/cms/templates/cms/{admin,vendor}/ |
✅ |
| Static | app/modules/cms/static/{admin,vendor}/js/ |
✅ |
| Routes | app/modules/cms/routes/{api,pages}/ |
✅ |
CMS Module Structure
app/modules/cms/
├── __init__.py # Lazy getter to avoid circular imports
├── definition.py # ModuleDefinition with self-contained config
├── exceptions.py # CMSException, ContentPageNotFoundError
├── locales/
│ ├── en.json
│ ├── fr.json
│ ├── de.json
│ └── lb.json
├── models/
│ ├── __init__.py # Exports: ContentPage, MediaFile, ProductMedia
│ └── content_page.py # ContentPage model (canonical location)
├── routes/
│ ├── __init__.py
│ ├── admin.py # Admin router wrapper
│ ├── vendor.py # Vendor router wrapper
│ ├── api/
│ │ ├── admin.py # Admin API endpoints
│ │ ├── vendor.py # Vendor API endpoints
│ │ └── shop.py # Shop/public API endpoints
│ └── pages/
│ ├── admin.py # Admin page routes
│ └── vendor.py # Vendor page routes
├── schemas/
│ ├── __init__.py
│ └── content_page.py # Pydantic schemas
├── services/
│ ├── __init__.py
│ └── content_page_service.py
├── static/
│ ├── admin/js/
│ │ ├── content-pages.js
│ │ └── content-page-edit.js
│ └── vendor/js/
│ ├── content-pages.js
│ └── content-page-edit.js
└── templates/
└── cms/
├── admin/
│ ├── content-pages.html
│ └── content-page-edit.html
└── vendor/
├── content-pages.html
└── content-page-edit.html
Key Patterns Established
1. Module-First Models
Models live in module folders and are dynamically loaded at startup:
# app/modules/cms/models/content_page.py (canonical location)
from app.core.database import Base
class ContentPage(Base):
__tablename__ = "content_pages"
...
# models/database/__init__.py (dynamic loader)
def _discover_module_models():
for module_dir in sorted(modules_dir.iterdir()):
models_init = module_dir / "models" / "__init__.py"
if models_init.exists():
importlib.import_module(f"app.modules.{module_dir.name}.models")
_discover_module_models()
2. Shared Templates Instance
Route files must import from app.templates_config:
# CORRECT
from app.templates_config import templates
# WRONG - creates local instance without module loaders
templates = Jinja2Templates(directory="app/templates")
3. Template Namespacing
Module templates use namespace prefix to avoid collisions:
# Module templates at: app/modules/cms/templates/cms/admin/content-pages.html
# Rendered as:
templates.TemplateResponse("cms/admin/content-pages.html", ...)
4. Import Pattern
All code should import from module:
# Models
from app.modules.cms.models import ContentPage
# Services
from app.modules.cms.services import content_page_service
# Exceptions
from app.modules.cms.exceptions import ContentPageNotFoundException
5. Lazy Imports for Circular Import Prevention
# app/modules/cms/__init__.py
def get_cms_module():
"""Lazy getter for cms_module to avoid circular imports."""
from app.modules.cms.definition import cms_module
return cms_module
Decisions Made
| Decision | Choice | Rationale |
|---|---|---|
| Pilot Module | CMS | Simplest, minimal dependencies |
| Cross-Module Pattern | Protocol pattern | Type-safe interfaces |
| Timeline | Incremental | Alongside feature work |
| Backwards Compatibility | No shims | Pre-launch, can delete old files |
| Template Namespace | {module}/admin/, {module}/vendor/ |
Prevent collisions |
Verification Completed
python -c "from main import app"succeeds- ContentPage model in
app/modules/cms/models/content_page.py - Dynamic model loader in
models/database/__init__.py content_pagestable in Base.metadata (67 total tables)- Template files in correct locations
- Route files use shared templates instance
- Admin CMS pages render correctly
- Vendor CMS pages render correctly
What Stays in Core vs Moves to Modules
Core (Stays in Place)
| Component | Location | Reason |
|---|---|---|
| User, Vendor, Company, Platform models | models/database/ |
Foundational entities |
| Auth service | app/services/ |
Cross-cutting concern |
| Storage, Cache, Email services | app/services/ |
Infrastructure utilities |
| Base exceptions | app/exceptions/ |
Shared error types |
| Shared macros, partials | app/templates/shared/ |
Reusable UI components |
| API dependencies | app/api/deps.py |
Auth, module access checks |
Modules (Move to Self-Contained)
| Module | Services | Models | Status |
|---|---|---|---|
| cms | content_page, media | content_page, media | ✅ Complete |
| billing | billing, stripe, invoice, subscription | subscription, invoice, payment | Pending |
| inventory | inventory, inventory_transaction | inventory, inventory_transaction | Pending |
| orders | order, cart, order_item_exception | order, order_item_exception | Pending |
| marketplace | marketplace, marketplace_product, letzshop_export | marketplace_product, import_job | Pending |
| customers | customer, customer_address | customer | Pending |
| messaging | messaging, notification | message, notification | Pending |
| analytics | stats, capacity_forecast | (uses other models) | Pending |
| monitoring | background_tasks, test_runner, log | test_run, architecture_scan | Pending |
Pending/Next Steps
Phase 3: Simple Modules Migration
- Migrate analytics module
- Migrate monitoring module
- Migrate messaging module
- Migrate customers module
Phase 4: Complex Modules Migration
- Migrate billing (with Stripe integration)
- Migrate inventory
- Migrate orders
- Migrate marketplace
Phase 5: Cleanup
- Remove deprecated shims (if any created)
- Update all imports across codebase
- Delete
app/platforms/directory - Update architecture documentation
Other Pending Items
- Wire up vendor module routers to
app/api/v1/vendor/__init__.py - PlatformModule database table (optional - for audit trail)
- Module-specific configuration UI
- Integration tests for
/api/v1/admin/modules/*endpoints
Git Commits
ec4ec04 feat: complete CMS as fully autonomous self-contained module
0b65864 fix: resolve circular import in CMS module
3ffa890 fix: correct static file mount order and update architecture validator
3307205 feat: add module info and configuration pages to admin panel
Key Files Reference
Self-Contained Module Infrastructure
app/modules/base.py- ModuleDefinition with self-contained flagsapp/modules/contracts/- Protocol definitions for cross-module depsapp/templates_config.py- Multi-directory template loadermodels/database/__init__.py- Dynamic module model discovery
CMS Module (Pilot)
app/modules/cms/definition.py- Module metadataapp/modules/cms/models/content_page.py- ContentPage modelapp/modules/cms/services/content_page_service.py- Business logicapp/modules/cms/exceptions.py- Module-specific exceptionsapp/modules/cms/templates/cms/- Namespaced templates
Testing
# Verify app starts
python -c "from main import app; print('OK')"
# Run module service tests
python -m pytest tests/unit/services/test_module_service.py -v
# Verify CMS model is loaded
python -c "from app.modules.cms.models import ContentPage; print(ContentPage.__tablename__)"
# Verify template loading
python -c "from app.templates_config import templates; print(templates.env.loader)"