Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade SQLAlchemy to 2.0.x #431

Merged
merged 3 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: "3.8"
version: "3.9"
services:
elasticsearch:
image: elasticsearch:7.12.0
image: elasticsearch:8.7.0
environment:
- xpack.security.enabled=false
- discovery.type=single-node
Expand All @@ -21,7 +21,7 @@ services:
- 9200:9200

kibana:
image: kibana:7.12.0
image: kibana:8.7.0
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
ports:
Expand All @@ -30,14 +30,14 @@ services:
- elasticsearch

redis:
image: redis:6.0.9-buster
image: redis:7.0.11
ports:
- "6379:6379"
environment:
- ALLOW_EMPTY_PASSWORD=yes

postgres:
image: postgres:11
image: postgres:15.2
ports:
- "5432:5432"
environment:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ domain events.

Command Handlers are defined with the `Domain.command_handler` decorator:

```python hl_lines="19-22 46-52"
{! docs_src/guides/exposing-domain/002.py !}
```python hl_lines="20-23 47-53"
{! docs_src/guides/access-domain/002.py !}
```

## Workflow
Expand Down Expand Up @@ -78,7 +78,10 @@ Each command handler method is wrapped in a `UnitOfWork` context, without
having to explicitly specify it. Both handler methods in
`AccountCommandHandler` below are equivalent:

```python hl_lines="5"
```python hl_lines="8"
from protean import handle, UnitOfWork


@domain.command_handler(part_of=Account)
class AccountCommandHandler:
@handle(RegisterCommand)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ to eventually make the rest of the system consistent.
A command is defined with the `Domain.command` decorator:

```python hl_lines="12-15"
{! docs_src/guides/exposing-domain/001.py !}
{! docs_src/guides/access-domain/001.py !}
```

A command is always associated with an aggregate class with the `part_of`
Expand Down
34 changes: 29 additions & 5 deletions docs/guides/domain-behavior/domain-services.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ associated with at least two aggregates with the `part_of` option.

The service methods in a Domain Service can be structured in three flavors:

### Class methods
### 1. Class with class methods

If you don't have any invariants to be managed by the Domain Service, each
method in the Domain Service can simply be a class method, that receives all
Expand All @@ -52,7 +52,7 @@ Invoking it is straight forward:
OrderPlacementService.place_order(order, inventories)
```

### Instance methods
### 2. Class with instance methods

In this flavor, the Domain Service is instantiated with the aggregates and each
method performs a distinct business function.
Expand All @@ -69,7 +69,7 @@ service = OrderPlacementService(order, inventories)
service.place_order()
```

### Callable class
### 3. Callable class

If you have a single business function, you can simply model it as a callable
class:
Expand All @@ -83,9 +83,27 @@ service = OrderPlacementService(order, inventories)
service()
```

### Deciding between different flavors

The decision between a class with instance methods and a callable class boils
down to two factors:
1. How many business functions does the Domain Service have? If it has only one,
then a callable class is more elegant.
2. Do you have `pre` invariants that only apply to specific methods? Then it
makes sense to construct each method as a separate callable class. If your
invariant methods apply to all methods, then a class with instance methods is
preferable.

As usual, you will probably have not enough insight to take this decision
upfront. As your domain model matures, review regularly and decide on the best
way to model the Domain Service.

!!!note
You can include private methods in a Domain Service class by prefixing the
method name with an underscore.
method name with an underscore. If you encounter a `RecursionError:
maximum recursion depth exceeded` error, it is likely that a domain method
is calling a private method, but the private method name is not prefixed
with an underscore.

## Typical Workflow

Expand Down Expand Up @@ -117,7 +135,13 @@ the service method. Unlike in Aggregates though, invariants in Domain Services
typically deal with validations that span across multiple aggregates.

`pre` invariants check the state of the aggregates before they are mutated,
while `post` invariants check the state after the mutation.
while `post` invariants check the state after the mutation.

!!!note
It is a good practice to step back and review the business logic placed in
the Domain Service now and then. If an invariant does not use multiple
aggregates, it is likely that it belongs within an aggregate and not in the
service.

## A full-blown example

Expand Down
5 changes: 0 additions & 5 deletions docs/guides/domain-behavior/invariants.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,6 @@ invariants are triggered after the update. `pre` invariants are used to prevent
invalid state from being introduced, while `post` invariants ensure that the
aggregate remains in a valid state after the update.

In Protean, we will mostly be using `post` invariants because the domain model
is expected to remain valid after any operation. You would typically start
with the domain in a good state, mutate the elements, and check if all
invariants are satisfied.

`pre` invariants are useful in certain situations where you want to check state
before the elements are mutated. For instance, you might want to check if a
user has enough balance before deducting it. Also, some invariant checks may
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from protean.globals import current_domain
from protean.fields import DateTime, Identifier, String

publishing = Domain(__file__, "Publishing")
publishing = Domain(__file__, "Publishing", load_toml=False)


def utc_now():
Expand Down
Loading
Loading