New Module
info
Learn how to create a new domain module in Fluvius Framework.
Creating a New Domain
Step 1: Create Domain Folder
Create a folder for your domain:
mkdir -p src/myapp/domains/user
cd src/myapp/domains/user
Step 2: Define State Model
Create state.py:
from fluvius.data import DataModel, field
from datetime import datetime
class UserState(DataModel):
name: str = field()
email: str = field()
active: bool = field(initial=True)
created_at: datetime = field(default_factory=datetime.now)
updated_at: datetime = field(default_factory=datetime.now)
Step 3: Define Aggregate
Create aggregate.py:
from fluvius.domain import Aggregate, action
from .state import UserState
class UserAggregate(Aggregate):
def __init__(self, domain):
super().__init__(domain)
self._state = None
@action(evt_key='user-created', resources=['user'])
async def create_user(self, name: str, email: str):
# Business logic
if not email or '@' not in email:
raise ValueError("Invalid email")
self._state = UserState(name=name, email=email, active=True)
return self._state
@action(evt_key='user-updated', resources=['user'])
async def update_user(self, name: str = None, email: str = None):
if self._state is None:
raise ValueError("User not created")
updates = {}
if name is not None:
updates['name'] = name
if email is not None:
if '@' not in email:
raise ValueError("Invalid email")
updates['email'] = email
updates['updated_at'] = datetime.now()
self._state = self._state.update(**updates)
return self._state
@action(evt_key='user-deactivated', resources=['user'])
async def deactivate_user(self):
if self._state is None:
raise ValueError("User not created")
self._state = self._state.update(active=False, updated_at=datetime.now())
return self._state
Step 4: Define Domain
Create domain.py:
from fluvius.domain import Domain
from .aggregate import UserAggregate
class UserDomain(Domain):
__aggregate__ = UserAggregate
class Meta:
revision = 1
tags = ["user", "identity"]
title = "User Management Domain"
description = "Domain for managing user accounts"
Step 5: Export Domain
Create __init__.py:
from .domain import UserDomain
from .aggregate import UserAggregate
from .state import UserState
__all__ = ['UserDomain', 'UserAggregate', 'UserState']
Using the Domain
Register with FastAPI
from fluvius.fastapi import FluviusFastAPI
from myapp.domains.user import UserDomain
app = FluviusFastAPI()
app.register_domain(UserDomain)
Use Domain Directly
from fluvius.domain.context import SanicContext
from myapp.domains.user import UserDomain
from fluvius.data import UUID_GENR
# Create context
ctx = SanicContext.create(namespace='app-user')
# Create domain
domain = UserDomain(ctx)
# Set aggregate root
user_id = UUID_GENR()
domain.set_aggroot('user', user_id)
# Create command
command = domain.create_command('create-user', {
'name': 'John Doe',
'email': 'john@example.com'
})
# Process command
response = await domain.process_command(command)
Complete Example
File Structure
myapp/
└── domains/
└── user/
├── __init__.py
├── domain.py
├── aggregate.py
└── state.py
Complete Code
state.py:
from fluvius.data import DataModel, field
from datetime import datetime
class UserState(DataModel):
name: str = field()
email: str = field()
active: bool = field(initial=True)
created_at: datetime = field(default_factory=datetime.now)
aggregate.py:
from fluvius.domain import Aggregate, action
from .state import UserState
class UserAggregate(Aggregate):
def __init__(self, domain):
super().__init__(domain)
self._state = None
@action(evt_key='user-created', resources=['user'])
async def create_user(self, name: str, email: str):
self._state = UserState(name=name, email=email, active=True)
return self._state
domain.py:
from fluvius.domain import Domain
from .aggregate import UserAggregate
class UserDomain(Domain):
__aggregate__ = UserAggregate
init.py:
from .domain import UserDomain
from .aggregate import UserAggregate
from .state import UserState
__all__ = ['UserDomain', 'UserAggregate', 'UserState']
Best Practices
1. Keep Domains Focused
Each domain should have a single responsibility:
# Good: Focused domain
class UserDomain(Domain):
# User management only
pass
# Bad: Too many responsibilities
class UserOrderPaymentDomain(Domain):
# Too many concerns
pass
2. Use Meaningful Names
Use clear, descriptive names:
# Good: Clear names
@action(evt_key='user-account-activated')
# Bad: Unclear names
@action(evt_key='user-updated')
3. Validate in Aggregates
Enforce business rules in aggregates:
@action(evt_key='user-created')
async def create_user(self, name: str, email: str):
# Validate business rules
if not email or '@' not in email:
raise ValueError("Invalid email")
pass
4. Use Type Hints
Always use type hints:
@action(evt_key='user-created')
async def create_user(self, name: str, email: str) -> dict:
return {'name': name, 'email': email}
Next Steps
- Learn about Add API Endpoint
- Explore Register Services
- Check Error Handling