A decoupled, Domain-Driven Design (DDD) e-commerce core built to demonstrate advanced software engineering and software architecture patterns in Python. Rather than following standard Django tutorial practices (tightly coupled views, string-based HTML templates, and fat models), this codebase utilizes custom core enginesβdjango-abstract and probo-uiβto achieve complete isolation of concerns, compile-time type safety for UI, and high-performance server-side state orchestration.
The primary objective of this architecture is uncompromised modularity. In standard Django, views directly query the database, parse HTML strings, and mutate state, leading to a highly coupled codebase that is difficult to scale, test, or refactor.
This platform completely decouples the Persistence layer, the Business Domain layer, and the User Interface layer:
graph TD
%% User/Browser Interactions
Browser[π User Browser] -->|HTTP Request / HTMX Ajax| Nginx[π Reverse Proxy / Traefik]
Nginx -->|Route Request| DjangoWSGI[π Gunicorn / Django WSGI]
%% Middleware Processing
subgraph DjangoPipeline ["Django Request & Middleware Pipeline"]
DjangoWSGI --> GlobalSession[1. GlobalSessionMiddleware<br>Injects session context]
GlobalSession --> CsrfContext[2. CsrfContextMiddleware<br>Hydrates Probo UI global token]
CsrfContext --> EntryMiddleware[3. EntryMiddleware<br>Hydrates Domain Master Entry]
end
%% Routing to Views
EntryMiddleware -->|Hydrated request.django_abstract_entry| View[π Django Domain View]
%% Domain DDD Layer
subgraph BusinessLogicLayer ["Domain-Driven Business Layer (django-abstract)"]
View -->|Injects context| Dependency[π App Dependency Container<br>Resolves Selectors & Creators]
View -->|Executes mutating action| Service[π οΈ Model Service / Bare Service]
Service -->|Validation checks| Validator[π Service Validator<br>Asserts business rules & boundaries]
Validator -->|Reads data| Selector[π Dynamic Selector<br>Retrieves Model objects]
Validator -->|Mutates state| Creator[βοΈ Dynamic Creator<br>Inserts/Updates records]
Selector -->|ORM Queries| DB[(πΎ PostgreSQL / SQLite)]
Creator -->|ORM Mutate| DB
end
%% UI Rendering Layer
subgraph PresentationLayer ["Component-Driven Python UI (probo-ui)"]
View -->|Supplies domain dictionary| Page[π Probo Page Component]
Page -->|Assembles| Component[π§© UI Components & Cards]
Component -->|SSDOM Manipulation| Element[π·οΈ Python HTML Elements]
Element -->|JIT styling compilation| CSS[π¨ Probo StyleManager]
Element -->|Generates string| RawHTML[π Optimized HTML / HTMX DOM payload]
end
%% Response Delivery
RawHTML -->|HttpResponse| Browser
%% Background Work
Service -->|Dispatches async event| RedisBroker[π¬ Redis Queue / Broker]
RedisBroker -->|Consume task| CeleryWorker[βοΈ Celery Workers]
CeleryWorker -->|Mutate state| DB
To eliminate repetitive boilerplates for model queries (Selectors) and record insertions (Creators), the system uses a custom class decorator pattern. By decorating a model with @creator_selector, the system dynamically creates dedicated, isolated selector and creator classes at import time and registers them in a centralized domain container.
# apps/product/models.py
from django_abstract.registry import creator_selector
from apps.product.dependencies import ProductAppDependecy
@creator_selector(dependency=ProductAppDependecy)
class ProductVariant(BaseModel):
"""Represents a specific variant of a product."""
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='variants')
sku = models.CharField(max_length=100, unique=True, null=True)
base_price = models.DecimalField(max_digits=10, decimal_places=2)
stock = models.PositiveIntegerField(default=0)
is_active = models.BooleanField(default=True)At runtime, developers access these selector instances cleanly through the resolved dependency container without direct database importing:
# apps/product/views/client_views/crud_views.py
class ProductCatalogView(View):
_dependency = get_product_app_dependency()
def get(self, request):
# The selector is dynamically instantiated and bound via __getattr__ interception
active_variants = self._dependency.select_product_variant.model_class.objects.filter(is_active=True)
# ...Under the hood, BaseDependency intercepts access using Python's __getattr__ magic method, fetching the correct dynamic class from the GLOBAL_REGISTRY bucket corresponding to the app domain:
# django_abstract/src/django_abstract/base/base_dependency.py
def __getattr__(self, item):
if hasattr(self, 'selectors') and item in self.selectors:
return self.selectors[item]() # Instantiates the dynamic Selector subclass
if hasattr(self, 'creators') and item in self.creators:
return self.creators[item]() # Instantiates the dynamic Creator subclass
# ...State mutations do not live in models or views. The system separates reads and writes:
- Selectors: Perform pure, read-only queries (e.g.
get_by(),filter_by()). - Services & Validators: Enforce strict mutation logic.
Every model service implements a nested BaseServiceValidator specifying minimum required fields, allowed schema fields, and reserved database transactions:
# apps/cart/services/cart_service.py
@register_service()
class CartItemModelService(BaseModelService):
model_dependency = CartAppDependency()
model_slug = "cart_item"
class CartItemServiceValidator(BaseModelService.BaseServiceValidator):
def meta_hook(self):
# Strict validation constraints for DB operations
self.MINIMUM_WRITE_FIELDS = ["cart", "product_variant", "quantity"]
self.SERVICE_DOMAIN_FIELDS = ["session", "pk", "product_variant", "quantity"]
self.VALID_FIELDS_PER_ACTION = {
"add_item_to_cart": ["product_variant", "quantity", "session"],
"remove_item_from_cart": ["pk"],
}
self.regester_method("add_item_to_cart", self.add_item_to_cart)
self.regester_method("remove_item_from_cart", self.remove_item_from_cart)
def add_item_to_cart(self):
product_variant_id, quantity, session = self.get_method_args("add_item_to_cart")
cart = self.dependency.select_cart.get_by(session=session)
product = get_product_app_dependency().select_product_variant.get_by(id=product_variant_id)
if product.stock < quantity:
raise ServiceException("Insufficient stock available")
cart_item, _ = self.dependency.create_cart_item.access_db.get_or_create(
product_variant=product, cart=cart
)
cart_item.quantity += quantity
cart_item.save()Instead of string-based parsing (like Django Templates) or heavy JavaScript-centric Single-Page Application (SPA) client rendering (like React/Vue), the UI is built using Probo UI, a backend-first UI framework written in pure, type-safe Python.
Rather than rendering plain strings directly, Probo UI constructs a live object tree in memory (SSDOM), allowing developers to manipulate node properties, inject scripts, and append dynamic nodes after layout definition but prior to serialization.
# ui/pages/product/client/product_catalog.py
from ui.pages.base import get_client_base_template
from probo import DIV, H1
from ui.components.product.client.product_catalog import ProductCatalog
class ProductCatalogPage:
def __init__(self, products=None, categories=None):
self.template = get_client_base_template()
self.products = products or []
self.categories = categories or []
self._title = "Store Catalog"
def render(self):
# 1. SSDOM Search: Find TITLE tag and rewrite inner HTML dynamically
base_title = self.template.html_doc.find(lambda n: n.tag == 'TITLE')
if base_title:
base_title.inner_html(self._title)
# 2. Compile component instance
product_list = ProductCatalog(self.products, self.categories)
# 3. SSDOM Search: Find matching node in the template tree and inject the component
root_container = self.template.html_doc.find(
lambda n: n.attr_manager.get_attr("data_ssdom_id") == 'root-container'
)
if root_container:
root_container.add(DIV(product_list))
# 4. Serialize the final SSDOM tree into standard, minified HTML
return self.template.render()Interactive featuresβlike updating cart quantities, checking out, and searching filtersβharness native HTMX integration within the Python UI components. Out-of-band (OOB) swaps are returned natively from views to update disparate sections of the page layout asynchronously (e.g. updating the cart subtotal inside the header nav while modifying quantity in the cart table list).
# ui/components/product/client/product_catalog.py
def ProductsSection(products, hx_oob=False):
return DIV(
DIV(
*[DIV(ProductCard(p), Class="col-md-4 mb-4") for p in products],
Class="row"
),
Class="col-md-9",
Id="product-grid-container",
hx_swap_oob="true" if hx_oob else False, # Declares an OOB swap to HTMX
)- Runtime Core: Python 3.12, Django 5.0
- Task Scheduling: Celery 5.5 (Distributed asynchronous event/state tasks)
- Caching & Broker: Redis (Used as a Django cache store and Celery AMQP broker)
- Database Engine: PostgreSQL (Production) / SQLite (Local development)
- Code Verification:
- Mypy: Typed static checking with
django-stubsplugin - Ruff: Ultrafast Python linter and formatter running on 70+ strict rule-sets
- Pytest & Coverage: Automated testing suite with DB reusability
- Mypy: Typed static checking with
pythonic-ecommerce/
β
βββ apps/ # Django domain applications
β βββ cart/ # Cart business domain and state services
β βββ checkout/ # Checkout domain and Stripe/payment systems
β βββ client/ # Client authentication, user links, middlewares
β βββ cms/ # Admin dashboard controllers and layout managers
β βββ order/ # Order placement, receipt generation, and archiving
β βββ product/ # Catalog search, pricing engine, variant mapping
β
βββ pythonic_ecommerce/ # Django configuration root
β βββ settings.py # Main configuration file (middlewares, database, logging)
β βββ celery_app.py # Celery broker initialization and configuration
β βββ urls.py # Global route dispatcher
β
βββ ui/ # Frontend layout architecture (probo-ui)
β βββ components/ # Reusable UI elements (cards, headers, filters)
β βββ pages/ # Main page layouts (storefront, detail pages)
β
βββ devops/ # Infrastructure orchestrations
β βββ compose/ # Docker Compose build configurations
β β βββ local/ # Django, Postgres, Redis, Celery, Flower for local
β β βββ production/ # Traefik, Django, Postgres, Redis, Celery for prod
β βββ .envs/ # Environment config files
β
βββ requirements/ # Strict dependency files divided by environment
βββ manage.py # Django CLI manager
βββ pyproject.toml # Mypy, Ruff, Pytest configurations
βββ README.md # This developer handbookThis repository provides a production-ready containerized environment. No local Python or Database installation is required to boot the application.
Ensure you have Docker and Docker Compose (v2+) installed:
docker --version
docker compose versionSpin up the backend app, database service, redis cache, celery workers, and Celery Flower monitoring dashboard:
docker compose -f docker-compose.local.yml up --build -dExecute migrations and create your default superuser account:
docker compose -f docker-compose.local.yml exec django python manage.py migrate
docker compose -f docker-compose.local.yml exec django python manage.py createsuperuser- Web Storefront: http://localhost:8000
- Flower Dashboard: http://localhost:5555 (Celery task monitoring)
Automated testing is configured via Pytest. Run the test suite within the running application container:
docker compose -f docker-compose.local.yml exec django pytestTo run coverage reports:
docker compose -f docker-compose.local.yml exec django coverage run -m pytest
docker compose -f docker-compose.local.yml exec django coverage report -m