Metadata-Version: 2.4
Name: modwire-hex
Version: 0.1.0
Summary: Explicit dependency-injection conventions for hexagonal Django applications
Keywords: django,dependency-injection,hexagonal-architecture,modwire
Author: Tomasz Szpak
Author-email: Tomasz Szpak <tomszp@gmail.com>
License-Expression: MIT
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development
Requires-Dist: django>=6.0.7
Requires-Dist: django-ninja-extra>=0.31.5
Requires-Dist: jsonschema>=4.26.0
Requires-Dist: wireup>=2.12.0
Requires-Dist: build>=1.3 ; extra == 'dev'
Requires-Dist: dirty-equals>=0.11 ; extra == 'dev'
Requires-Dist: pgvector>=0.5.0 ; extra == 'dev'
Requires-Dist: psycopg[binary]>=3.3.4 ; extra == 'dev'
Requires-Dist: pytest-django>=4.11 ; extra == 'dev'
Requires-Dist: pytest>=9 ; extra == 'dev'
Requires-Dist: ruff>=0.14 ; extra == 'dev'
Requires-Dist: twine>=6 ; extra == 'dev'
Requires-Python: >=3.14
Project-URL: Homepage, https://github.com/modwire/modwire-hex
Project-URL: Repository, https://github.com/modwire/modwire-hex
Project-URL: Issues, https://github.com/modwire/modwire-hex/issues
Provides-Extra: dev
Description-Content-Type: text/markdown

# modwire-hex

Explicit dependency-injection conventions for hexagonal Django applications,
built on [Wireup](https://maldoinc.github.io/wireup/).

## Install

```bash
pip install modwire-hex
```

The framework keeps the container at the composition root. Domain and
application code use ordinary, type-annotated constructor dependencies.

Domain code may use `DomainError` for business-rule failures. It must not
contain HTTP status codes, response shapes, Django types, or DI concerns.

```python
from modwire_hex import DomainError


class OrderCannotRequestCredit(DomainError):
    pass
```

```python
from modwire_hex import Containers, Module, Providers


class Payments:
    def charge(self, amount: int) -> None: ...


class StripePayments(Payments):
    def charge(self, amount: int) -> None:
        ...


class PlaceOrder:
    def __init__(self, payments: Payments) -> None:
        self.payments = payments


orders = Module(
    "orders",
    providers=[
        Providers.service(PlaceOrder, None),
        Providers.bind(Payments, StripePayments, "singleton", None),
    ],
)

container = Containers.create_sync((orders,), None)
with container.enter_scope() as scope:
    place_order = scope.get(PlaceOrder)
```

See [ADR 0001](docs/adr/0001-explicit-modules-over-discovery.md) for the
architecture, conventions, and delivery sequence.

## Django integration

Compose a Django process through one explicit application object:

```python
from modwire_hex import DjangoApplication

from .orders.wiring import orders


application = DjangoApplication(modules=(orders,))
```

Configure the framework and its Ninja Extra API in Django settings:

```python
INSTALLED_APPS = [
    "modwire_hex.django.apps.ModwireConfig",
]

MIDDLEWARE = [
    "modwire_hex.django.middleware.RequestScopeMiddleware",
]

MODWIRE = {
    "APPLICATION": "myproject.wiring:application",
    "NINJA": {"title": "My Project API", "version": "1.0.0"},
}
```

Mount the framework API once. Ninja Extra discovers controllers from installed
HTTP adapter apps:

```python
from django.urls import path

from modwire_hex.django import DjangoNinja


urlpatterns = [path("api/", DjangoNinja.api().urls)]
```

At an HTTP boundary, resolve only use cases from the active request scope:

```python
from modwire_hex.django import DjangoRequest


place_order = DjangoRequest.resolve(request, PlaceOrder)
```

`DjangoRepository` is a base for outbound adapters. It centralizes generic
load and create-or-update persistence mechanics; each context repository owns
its key, lookup, record mapping, and absence semantics.
