Hybrid Modular DDD
Here’s a full, concrete file structure you can adopt for mbpanel right now.
I’m going to assume:
- Single repo: Next.js frontend + FastAPI backend
- Backend uses domains + modules + infrastructure as we discussed
- Not going insane with microservices yet (one backend app)
You can prune later, but this gives you a complete, opinionated baseline.
Top-level¶
mbpanel/
├── README.md
├── .gitignore
├── .env.example
├── docker/
│ ├── backend.Dockerfile
│ ├── frontend.Dockerfile
│ └── docker-compose.yml
├── infra/ # optional: k8s/terraform/etc. later
│ ├── k8s/
│ └── terraform/
├── frontend/ # Next.js app
└── backend/ # FastAPI app
Frontend (Next.js, feature-based)¶
mbpanel/frontend/
├── package.json
├── pnpm-lock.yaml / package-lock.json / yarn.lock
├── next.config.mjs
├── tsconfig.json
├── .eslintrc.cjs
├── .prettierrc
├── public/
│ ├── favicon.ico
│ ├── logo.svg
│ └── ...static assets...
└── src/
├── app/ # Next.js App Router
│ ├── layout.tsx
│ ├── page.tsx # dashboard / landing
│ └── (routes)/ # if you use route groups
├── features/ # DOMAIN-ALIGNED FEATURE MODULES
│ ├── auth/
│ │ ├── components/
│ │ ├── hooks/
│ │ ├── pages/ # or route segments under app/
│ │ └── types.ts
│ ├── users/
│ ├── teams/
│ ├── sites/
│ ├── environments/
│ ├── staging/
│ ├── backups/
│ ├── cache/
│ ├── domains/
│ ├── sftp/
│ ├── wordpress/
│ ├── payments/
│ ├── billing/
│ ├── uptime/
│ ├── nodes/
│ ├── activity/
│ ├── favourites/
│ ├── config/
│ ├── profile/
│ ├── sessions/
│ └── webhook/
└── shared/ # Shared/common frontend code
├── api/ # axios/fetch clients, react-query hooks
├── components/
├── hooks/
├── lib/ # utils, formatting
├── store/ # Zustand/Redux, etc.
└── types/
You can refine the internal structure of each features/* later (component-per-route, etc.), but this is enough to keep everything domain-aligned.
Backend – root¶
mbpanel/backend/
├── pyproject.toml # or requirements.txt + setup.cfg
├── requirements.txt # (if not using pyproject)
├── requirements-dev.txt
├── alembic.ini # if using Alembic
├── .env.example
├── .flake8 / pyproject lint config
├── .mypy.ini
├── .gitignore
├── app/
└── scripts/
Backend – scripts/ (you already listed)¶
mbpanel/backend/scripts/
├── migrate_data.py # MySQL → PostgreSQL migration
├── seed_dev_data.py # Development data seeding
├── health_check.py # Health check utilities
├── generate_module.py # Generate new module scaffold (domains/modules)
└── validate_architecture.py # Enforce boundaries/import rules
Backend – app/ layout¶
mbpanel/backend/app/
├── __init__.py
├── main.py # FastAPI app factory & entrypoint
├── core/ # cross-cutting application concerns
├── infrastructure/ # external systems, DB, queues, etc.
├── domains/ # CORE DDD-like domains
├── modules/ # supporting features (simpler)
├── api/ # HTTP composition/versioning layer
└── tests/
core/ – application-wide concerns¶
mbpanel/backend/app/core/
├── __init__.py
├── config.py # Pydantic settings, env vars
├── logging.py # logging config
├── security.py # JWT, password hashing, auth helpers
├── app_factory.py # create_app() builds FastAPI instance
├── middleware.py # CORS, request logging, etc.
├── dependencies.py # truly global Depends() (db, current_user)
└── identity/ # shared “kernel” concept for auth/tenancy
├── __init__.py
├── rbac.py # Role-based access control logic
├── tenant.py # Multi-tenant resolution, team_id logic
└── audit.py # Audit logging hooks/helpers
infrastructure/ – tech/integration (not domain rules)¶
mbpanel/backend/app/infrastructure/
├── __init__.py
├── database/
│ ├── __init__.py
│ ├── base.py # Base ORM model, metadata
│ ├── session.py # engine + SessionLocal / async_session
│ ├── models/ # optional shared models if needed
│ │ └── __init__.py
│ └── migrations/ # Alembic versions/
│ ├── env.py
│ └── versions/
├── external/
│ ├── __init__.py
│ ├── virtuozzo.py # API adapter for Virtuozzo platform
│ ├── bunny_cdn.py # API adapter for BunnyCDN
│ └── cloudflare.py # API adapter for Cloudflare
├── background/
│ ├── __init__.py
│ ├── celery_app.py / rq_app.py # worker configuration
│ └── tasks_common.py # shared job helpers
└── messaging/
├── __init__.py
├── websocket.py # websocket infra (FastAPI/Starlette)
└── events.py # event bus / domain-events plumbing (optional)
You can move jobs/websocket here instead of treating them as domains.
domains/ – core business domains (DDD-ish)¶
These are the ones where you care about state machines, invariants, orchestration.
mbpanel/backend/app/domains/
├── __init__.py
├── sites/
│ ├── __init__.py
│ ├── domain.py # Site entity, value objects
│ ├── schemas.py # Pydantic models for API
│ ├── service.py # use-cases: create_site, clone_site, delete_site
│ ├── repository.py # DB access for sites
│ └── router.py # APIRouter("/sites")
├── environments/
│ ├── __init__.py
│ ├── domain.py # Environment entity (dev/stage/prod)
│ ├── schemas.py
│ ├── service.py # create_env, promote_env, etc.
│ ├── repository.py
│ └── router.py
├── staging/
│ ├── __init__.py
│ ├── domain.py
│ ├── schemas.py
│ ├── service.py
│ ├── repository.py
│ └── router.py
├── backups/
│ ├── __init__.py
│ ├── domain.py # Backup entity, retention rules
│ ├── schemas.py
│ ├── service.py # schedule_backup, restore_backup
│ ├── repository.py
│ └── router.py
├── domains_management/
│ ├── __init__.py
│ ├── domain.py # HostedDomain, Certificate, DNS bindings
│ ├── schemas.py
│ ├── service.py # attach_domain, detach_domain, verify_dns
│ ├── repository.py
│ └── router.py
├── wordpress/
│ ├── __init__.py
│ ├── domain.py # WPInstall, Plugin, Theme, versions, states
│ ├── schemas.py
│ ├── service.py # install_wp, update_core, manage_plugins
│ ├── repository.py
│ └── router.py
├── payments/
│ ├── __init__.py
│ ├── domain.py # PaymentAttempt, TransactionState
│ ├── schemas.py
│ ├── service.py # charge, refund, verify_webhook (delegates gateway)
│ ├── repository.py
│ └── router.py
├── billing/
│ ├── __init__.py
│ ├── domain.py # Subscription, Plan, UsageMetric
│ ├── schemas.py
│ ├── service.py # create_subscription, upgrade, cancel, invoice
│ ├── repository.py
│ └── router.py
├── nodes/
│ ├── __init__.py
│ ├── domain.py # Node/Server entity (capacity, status)
│ ├── schemas.py
│ ├── service.py # assign_site_to_node, drain_node, etc.
│ ├── repository.py
│ └── router.py
└── sessions/
├── __init__.py
├── domain.py # Session entity, rules (expiry, revocation)
├── schemas.py
├── service.py
├── repository.py
└── router.py
You can add value_objects.py or exceptions.py inside each domain if you go heavier on DDD later.
modules/ – supporting features (lighter)¶
Here you don’t go full DDD; keep them clean but simpler.
mbpanel/backend/app/modules/
├── __init__.py
├── auth/
│ ├── __init__.py
│ ├── router.py # /auth/login, /auth/refresh, etc.
│ ├── schemas.py
│ └── service.py # wraps core.security, handles tokens
├── users/
│ ├── __init__.py
│ ├── router.py
│ ├── schemas.py
│ ├── repository.py
│ └── service.py
├── teams/
│ ├── __init__.py
│ ├── router.py
│ ├── schemas.py
│ ├── repository.py
│ └── service.py
├── activity/
│ ├── __init__.py
│ ├── router.py
│ ├── schemas.py
│ ├── repository.py
│ └── service.py # record_activity, list_activity
├── favourites/
│ ├── __init__.py
│ ├── router.py
│ ├── schemas.py
│ ├── repository.py
│ └── service.py
├── config/
│ ├── __init__.py
│ ├── router.py
│ ├── schemas.py
│ └── service.py
├── profile/
│ ├── __init__.py
│ ├── router.py
│ ├── schemas.py
│ └── service.py
├── uptime/
│ ├── __init__.py
│ ├── router.py
│ ├── schemas.py
│ ├── repository.py
│ └── service.py # check status, integrate external monitor
├── sftp/
│ ├── __init__.py
│ ├── router.py
│ ├── schemas.py
│ └── service.py # orchestrates infra.external/virtuozzo SFTP
└── webhook/
├── __init__.py
├── router.py # handles webhooks from payment gateway, etc.
├── schemas.py
└── service.py
If any of these grows into “true core domain” territory, you can move it from modules/ to domains/ later without breaking everything.
api/ – HTTP composition / versioning¶
FastAPI doesn’t care how you split routers as long as you include_router. This gives you a thin layer that wires the HTTP API to your domains/modules.
mbpanel/backend/app/api/
├── __init__.py
└── v1/
├── __init__.py
├── sites.py # includes domains.sites.router
├── environments.py
├── staging.py
├── backups.py
├── domains.py # from domains.domains_management.router
├── wordpress.py
├── payments.py
├── billing.py
├── nodes.py
├── sessions.py
├── auth.py # includes modules.auth.router
├── users.py
├── teams.py
├── activity.py
├── favourites.py
├── config.py
├── profile.py
├── uptime.py
├── sftp.py
└── webhook.py
Each of these api/v1/*.py files is basically:
from fastapi import APIRouter
from app.domains.sites.router import router as sites_router
router = APIRouter()
router.include_router(sites_router, prefix="/sites", tags=["sites"])
…and then main.py includes the api.v1.*.router.
main.py – entrypoint¶
Just so you see where it sits in the structure:
# mbpanel/backend/app/main.py
from fastapi import FastAPI
from app.core.app_factory import create_app
app: FastAPI = create_app()
and create_app() in core/app_factory.py wires up:
- middleware
- global deps
- includes all
api.v1.*.routermodules
tests/¶
mbpanel/backend/app/tests/
├── __init__.py
├── conftest.py # pytest fixtures
├── unit/
│ ├── test_sites_domain.py
│ ├── test_billing_domain.py
│ ├── test_wordpress_service.py
│ └── ...
└── integration/
├── test_api_sites.py
├── test_api_auth.py
├── test_background_jobs.py
└── ...