Skip to content

Commit

Permalink
Documentation Tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
subhashb committed Jun 21, 2024
1 parent 43b9a5c commit dc1dd99
Show file tree
Hide file tree
Showing 15 changed files with 329 additions and 268 deletions.
2 changes: 2 additions & 0 deletions .bumpversion.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ tag = True
[bumpversion:file:src/protean/template/domain_template/pyproject.toml.jinja]

[bumpversion:file:docs-sphinx/user/installation.rst]

[bumpversion:file:docs/guides/getting-started/installation.md]
2 changes: 1 addition & 1 deletion docs/guides/cli/index.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Command Line Interface
# CLI

When you install Protean, you also get a handy command line interface - the `protean` script - in your virtualenv. Driven by [Typer](https://typer.tiangolo.com), the script gives access to commands that can help you scaffold projects, generate new code, and run background servers. The `--help` option will give more information about any commands and options.

Expand Down
84 changes: 42 additions & 42 deletions docs/guides/compose-a-domain/activate-domain.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,48 +22,7 @@ thread's execution. The domain context keeps track of the domain-level data
during the lifetime of a domain object, and is used while processing handlers,
CLI commands, or other activities.

## Storing Data

The domain context also provides a `g` object for storing data. It is a simple
namespace object that has the same lifetime as an domain context.

!!! note
The `g` name stands for "global", but that is referring to the data
being global within a context. The data on `g` is lost after the context
ends, and it is not an appropriate place to store data between domain
calls. Use a session or a database to store data across domain model calls.

A common use for g is to manage resources during a domain call.

1. `get_X()` creates resource X if it does not exist, caching it as g.X.

2. `teardown_X()` closes or otherwise deallocates the resource if it exists.
It is registered as a `teardown_domain_context()` handler.

Using this pattern, you can, for example, manage a file connection for the
lifetime of a domain call:

```python
from protean.globals import g

def get_log():
if 'log' not in g:
g.log = open_log_file()

return g.log

@domain.teardown_appcontext
def teardown_log_file(exception):
file_obj = g.pop('log', None)

if not file_obj.closed:
file_obj.close()
```

Now, every call to `get_log()` during the domain call will return the same file
object, and it will be closed automatically at the end of processing.

## Pushing up the Domain Context
## Activating the Domain Context

A Protean domain is activated close to the application's entrypoint, like an
API request. In many other cases, like Protean's server processing commands and
Expand Down Expand Up @@ -110,3 +69,44 @@ context.pop()
!!! warning
If you do activate context manually, ensure you call context.pop() once the
task has been completed to prevent context leakage across threads.

## Storing Data

The domain context also provides a `g` object for storing data. It is a simple
namespace object that has the same lifetime as an domain context.

!!! note
The `g` name stands for "global", but that is referring to the data
being global within a context. The data on `g` is lost after the context
ends, and it is not an appropriate place to store data between domain
calls. Use a session or a database to store data across domain model calls.

A common use for g is to manage resources during a domain call.

1. `get_X()` creates resource X if it does not exist, caching it as g.X.

2. `teardown_X()` closes or otherwise deallocates the resource if it exists.
It is registered as a `teardown_domain_context()` handler.

Using this pattern, you can, for example, manage a file connection for the
lifetime of a domain call:

```python
from protean.globals import g

def get_log():
if 'log' not in g:
g.log = open_log_file()

return g.log

@domain.teardown_appcontext
def teardown_log_file(exception):
file_obj = g.pop('log', None)

if not file_obj.closed:
file_obj.close()
```

Now, every call to `get_log()` during the domain call will return the same file
object, and it will be closed automatically at the end of processing.
57 changes: 0 additions & 57 deletions docs/guides/compose-a-domain/domain-object.md

This file was deleted.

56 changes: 56 additions & 0 deletions docs/guides/compose-a-domain/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ It is responsible for creating and maintaining the object graph of all the
domain elements in the Bounded Context.

`Domain` class is the one-stop gateway to:

- Register domain elements
- Retrieve dynamically-constructed artifacts like repositories and models
- Access injected technology components at runtime
Expand All @@ -21,3 +22,58 @@ domain elements in the Bounded Context.
modules are composed together. It's the place where we instantiate objects
and their dependencies before the actual application starts running.

## Initialize the domain object

Constructing the object graph is a two-step procedure. First, you initialize a
domain object at a reasonable starting point of the application.

```py hl_lines="3"
{! docs_src/guides/composing-a-domain/001.py !}
```

## Parameters

### **`root_path`**

The mandatory `root_path` parameter is the directory containing the domain's
elements.

Typically, this is the folder containing the file initializing the domain
object. Protean uses this path to traverse the directory structure
and [auto-discover domain elements](#auto-discover-domain-elements) when the
domain is [initialized](#initialize-the-domain).

In the example below, the domain is defined in `my_domain.py`. Domain elements
are nested within the `src` folder, directly or in their own folders.

```shell
my_project
├── src
│ └── my_domain.py
│ └── authentication
│ └── user_aggregate.py
│ └── account_aggregate.py
├── pyproject.toml
├── poetry.lock
```

<!-- FIXME Create a "domain structuring" guide -->
Review the guide on structuring your domain for more information.

### **`name`**

The constructor also accepts an optional domain name to uniquely identify the
domain in the application ecosystem.

!!!note
When not specified, the name is initialized to the name of the module
defining the domain. Typically, this is the name of the file in which
the domain is defined, without the `.py` extension.

### **`identity_function`**

The function to use to generate unique identity values.

```py hl_lines="7-9 11 14"
{! docs_src/guides/composing-a-domain/022.py !}
```
9 changes: 4 additions & 5 deletions docs/guides/compose-a-domain/initialize-domain.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ domain.init(traverse=False)
```

If you choose to not traverse, Protean will not be able to detect domain
elements automatically. You are responsible for registering each element
with Protean explicitly.
elements automatically. ***You are responsible for registering each element
with the domain explicitly***.

## 2. Construct the object graph

Expand All @@ -41,14 +41,13 @@ a database that actually persists data.
Calling `domain.init()` establishes connectivity with the underlying infra,
testing access, and making them available for use by the rest of the system.

```python hl_lines="5-11"
```python hl_lines="5-9 11"
{! docs_src/guides/composing-a-domain/017.py !}
```

In the example above, the domain activates an SQLite database repository and
makes it available for domain elements for further use.

<!-- FIXME Add link to accessing active/configured dependencies -->
<!-- FIXME Add link to configuration handling -->
Refer to Configuration Handling to understand the different ways to configure
Refer to [Configuration Handling](configuration.md) to understand the different ways to configure
the domain.
36 changes: 19 additions & 17 deletions docs/guides/compose-a-domain/register-elements.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,36 @@ the domain.
A full list of domain decorators along with examples are available in the
[decorators](element-decorators.md) section.

## Explicit registration

You can also choose to register elements manually.

```python hl_lines="7-13"
{! docs_src/guides/composing-a-domain/014.py !}
```

Note that the `User` class has been subclassed from `BaseAggregate`. That is
how Protean understands the kind of domain element being registered. Each type
of element in Protean has a distinct base class of its own.

<!-- FIXME Add link to base classes -->

## Passing additional options

There might be additional options you will pass in a `Meta` inner class,
depending upon the element being registered.

```python hl_lines="12-13"
```python hl_lines="7"
{! docs_src/guides/composing-a-domain/015.py !}
```

In the above example, the `User` aggregate's default stream name - `user` - is
customized to `account`.
In the above example, the `User` aggregate's default stream name **`user`** is
customized to **`account`**.

Review the [object model](object-model.md) to understand
multiple ways to pass these options. Refer to each domain element's
documentation to understand the additional options supported by that element.

<!--FIXME Add info on how to get to each domain element -->

## Explicit registration

You can also choose to register elements manually.

```python hl_lines="7-10 13"
{! docs_src/guides/composing-a-domain/014.py !}
```

Note that the `User` class has been subclassed from `BaseAggregate`. That is
how Protean understands the kind of domain element being registered. Each type
of element in Protean has a distinct base class of its own.

Also, additional options are now passed in the `register` call to the domain.

<!-- FIXME Add link to base classes -->
6 changes: 3 additions & 3 deletions docs/guides/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ There are many version managers that help you create virtual environments,
but we will quickly walk through the steps to create a virtual environment with
one bundled with Python, `venv`.

## Create an environment
## Create a virtual environment

Create a project folder and a `.venv` folder within. Follow Python's
[venv guide](https://docs.python.org/3/library/venv.html) to install a new
Expand Down Expand Up @@ -55,7 +55,7 @@ Use the ``protean`` CLI to verify the installation:

```shell
$ protean --version
Protean 0.11.0
Protean 0.12.1
```

To verify that Protean can be seen by your current installation of Python,
Expand All @@ -65,7 +65,7 @@ try importing Protean from a ``python`` shell:
$ python
>>> import protean
>>> protean.get_version()
'0.11.0'
'0.12.1'
```

-------------------
Expand Down
2 changes: 0 additions & 2 deletions docs/guides/propagate-state/event-handlers.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ Out[8]: {
'in_stock': 90,
'id': '9272d70f-b796-417d-8f30-e01302d9f1a9'
}

In [9]:
```

## Configuration Options
Expand Down
Loading

0 comments on commit dc1dd99

Please sign in to comment.