Chuyển tới nội dung chính
Trang không công khai
Trang này không công khai. Công cụ tìm kiếm sẽ không đánh chỉ mục trang này và chỉ những ai có liên kết mới có thể truy cập trang.

Core Principles

thông tin

Fluvius Framework is built on proven principles from Domain-Driven Design, Event Sourcing, and modern software architecture.

1. Domain-Driven Design​

Business Logic First​

The domain model is the heart of your application. Business logic lives in aggregates, not in services or controllers.

Principle:

  • Business rules are enforced in aggregates
  • Domain logic is isolated from infrastructure
  • Code reflects business terminology

Example:

class AccountAggregate(Aggregate):
@action(evt_key='account-opened')
async def open_account(self, customer_id: str, initial_balance: float):
# Business rule: minimum balance required
if initial_balance < 100:
raise ValueError("Minimum balance is $100")
# Business logic here
return {'customer_id': customer_id, 'balance': initial_balance}

Ubiquitous Language​

Use the same terminology in code and business conversations.

Principle:

  • Code uses business terms
  • Domain experts understand the code
  • No translation layer needed

Example:

# Good: Uses business language
@action(evt_key='order-placed')
async def place_order(self, items: list):
pass

# Bad: Uses technical terms
@action(evt_key='order-created')
async def create_order(self, products: list):
pass

Bounded Contexts​

Clear boundaries between different parts of the system.

Principle:

  • Each domain is a bounded context
  • Domains can evolve independently
  • Clear interfaces between contexts

2. Event Sourcing​

Events as Facts​

Events represent things that have happened. They are immutable facts.

Principle:

  • Events are append-only
  • Events cannot be changed
  • Events can be replayed

Example:

@action(evt_key='user-created', resources=['user'])
async def create_user(self, name: str, email: str):
# Event 'user-created' is automatically generated
# It's immutable and stored forever
return {'name': name, 'email': email}

Event Log as Source of Truth​

The event log is the single source of truth. Current state is derived from events.

Principle:

  • All state changes are events
  • State can be rebuilt from events
  • Complete history is preserved

Time Travel​

Query state at any point in time by replaying events.

Principle:

  • Replay events up to a point
  • Query historical state
  • Debug by replaying events

3. Command Query Responsibility Segregation (CQRS)​

Separate Commands and Queries​

Commands modify state, queries read state. They use different models.

Principle:

  • Commands go through aggregates
  • Queries read from state store
  • Different models for reads and writes

Example:

# Command: Modify state
command = domain.create_command('create-user', {...})
response = await domain.process_command(command)

# Query: Read state
user = await domain.statemgr.fetch('user', user_id)

Independent Scaling​

Scale read and write operations independently.

Principle:

  • Read replicas for queries
  • Separate write instances
  • Optimize each path independently

4. Immutability​

Immutable Events​

Events are immutable. Once created, they never change.

Principle:

  • Events are append-only
  • No updates or deletes
  • Compensating events for corrections

Example:

# Good: Create new event for correction
@action(evt_key='user-email-corrected')
async def correct_email(self, new_email: str):
# Original event remains, new event corrects it
pass

# Bad: Trying to modify existing event
# This is not possible in Fluvius

Immutable State Snapshots​

State snapshots are point-in-time views.

Principle:

  • State is derived from events
  • Snapshots are read-only
  • New state comes from new events

5. Explicit State Management​

State is Explicit​

State is managed explicitly, not hidden in databases.

Principle:

  • State is defined in aggregates
  • State transitions are explicit
  • State changes generate events

Example:

class UserState(DataModel):
name: str = field()
email: str = field()
active: bool = field(initial=True)

class UserAggregate(Aggregate):
def __init__(self, domain):
super().__init__(domain)
self._state = UserState()

@action(evt_key='user-created')
async def create_user(self, name: str, email: str):
# Explicit state update
self._state = UserState(name=name, email=email, active=True)
return self._state

6. Testability​

Test in Isolation​

Aggregates can be tested without infrastructure.

Principle:

  • Test business logic separately
  • Mock infrastructure dependencies
  • Test events and state changes

Example:

async def test_create_user():
aggregate = UserAggregate(domain)
result = await aggregate.create_user('John', 'john@example.com')
assert result['name'] == 'John'
# Verify event was generated

7. Type Safety​

Type Hints Throughout​

Full type hint support for better IDE support and error detection.

Principle:

  • Type hints on all methods
  • Runtime validation with Pydantic
  • Better IDE autocomplete

Example:

from typing import Optional

class UserAggregate(Aggregate):
@action(evt_key='user-created')
async def create_user(self, name: str, email: str) -> dict:
return {'name': name, 'email': email}

8. Convention over Configuration​

Sensible Defaults​

Sensible defaults reduce boilerplate.

Principle:

  • Defaults work for most cases
  • Override when needed
  • Less configuration required

Example:

# Defaults work out of the box
class UserDomain(Domain):
__aggregate__ = UserAggregate
# No need to configure event store, state manager, etc.

9. Modularity​

Modular Architecture​

Framework is built from independent modules.

Principle:

  • Use only what you need
  • Modules are independent
  • Easy to extend

Example:

# Use only domain module
from fluvius.domain import Domain

# Or add FastAPI integration
from fluvius.fastapi import FluviusFastAPI

# Or add worker support
from fluvius.worker import Worker

10. Developer Experience​

Easy to Use​

Framework should be easy to use and understand.

Principle:

  • Clear API
  • Good documentation
  • Helpful error messages
  • Good tooling

Applying These Principles​

In Your Code​

  1. Start with Domain: Define aggregates first
  2. Use Events: All state changes generate events
  3. Separate Commands/Queries: Use CQRS pattern
  4. Test Aggregates: Test business logic in isolation
  5. Type Everything: Use type hints throughout

In Your Architecture​

  1. Clear Boundaries: Define bounded contexts
  2. Event-Driven: Use events for communication
  3. Scalable: Design for independent scaling
  4. Observable: Log events and state changes
  5. Maintainable: Keep code clean and organized

Next Steps​