Skip to content

Commit

Permalink
Support config for multiple environments (#448)
Browse files Browse the repository at this point in the history
Support config for multiple environments

This commit supports specifying configurations for multiple
environments in the `toml` configuration file. The active environment
is specified in the `PROTEAN_ENV` environment variable.
  • Loading branch information
subhashb authored Jul 25, 2024
1 parent 7c720ab commit 7ab79a1
Show file tree
Hide file tree
Showing 16 changed files with 638 additions and 122 deletions.
235 changes: 214 additions & 21 deletions docs/guides/configuration.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,157 @@
# Configuration

## Primary Configuration Attributes
Protean's configuration is specified in a `toml` file. A `domain.toml` file is
generated when you initialize a protean application with the
[`new`](./cli/new.md) command.

### `environment`
The configuration can be placed in a file named `.domain.toml` or `domain.toml`,
or can even leverage existing `pyproject.toml`. Protean looks for these files
- in that order - when the domain object is initialized.

The `environment` attribute specifies the current running environment of the
application. By default, the environment is `development`. The current
environment can be gathered from an environment variable called `PROTEAN_ENV`.
A sample configuration file is below:

Protean recognizes `development` and `production` environments, but additional
environments such as `pre-prod`, `staging`, and `testing` can be specified as
needed.
```toml
debug = true
testing = true
secret_key = "tvTpk3PAfkGr5x9!2sFU%XpW7bR8cwKA"
identity_strategy = "uuid"
identity_type = "string"
event_processing = "sync"
command_processing = "sync"

[databases.default]
provider = "memory"

[databases.memory]
provider = "memory"

[brokers.default]
provider = "inline"

[caches.default]
provider = "memory"

[event_store]
provider = "memory"

[custom]
foo = "bar"

[staging]
event_processing = "async"
command_processing = "sync"

[staging.databases.default]
provider = "sqlite"
database_url = "sqlite:///test.db"

[staging.brokers.default]
provider = "redis"
URI = "redis://staging.example.com:6379/2"
TTL = 300

[staging.custom]
foo = "qux"

[prod]
event_processing = "async"
command_processing = "async"

[prod.databases.default]
provider = "postgresql"
database_url = "postgresql://postgres:postgres@localhost:5432/postgres"

- **Default Value**: `development`
- **Environment Variable**: `PROTEAN_ENV`
- **Examples**:
- `development`
- `production`
- `pre-prod`
- `staging`
- `testing`
[prod.brokers.default]
provider = "redis"
URI = "redis://prod.example.com:6379/2"
TTL = 30

[prod.event_store]
provider = "message_db"
database_uri = "postgresql://message_store@localhost:5433/message_store"

[prod.custom]
foo = "quux"
```

## Basic Configuration Parameters

### `debug`

Specifies if the application is running in debug mode.

***Do not enable debug mode when deploying in production.***

Default: `False`

### `testing`

Enable testing mode. Exceptions are propagated rather than handled by the
domain’s error handlers. Extensions may also change their behavior to
facilitate easier testing. You should enable this in your own tests.

Default: `False`

### `secret_key`

## Domain Configuration Attributes
A secret key that will be used for security related needs by extensions or your
application. It should be a long random `bytes` or `str`.

You can generate a secret key with the following command:

```shell
> python -c 'import secrets; print(secrets.token_hex())'
c4bf0121035265bf44657217c33a7d041fe9e505961fc7da5d976aa0eaf5cf94
```

***Do not reveal the secret key when posting questions or committing code.***

### `identity_strategy`

The default strategy to use to generate an identity value. Can be overridden
at the [`Auto`](./domain-definition/fields/simple-fields.md#auto) field level.

Supported options are `uuid` and `function`.

If the `identity_strategy` is chosen to be a `function`, `identity_function`
has to be mandatorily specified during domain object initialization.

Default: `uuid`

### `identity_type`

The type of the identity value. Can be overridden
at the [`Auto`](./domain-definition/fields/simple-fields.md#auto) field level.

Supported options are `integer`, `string`, and `uuid`.

Default: `string`

### `command_processing`

Whether to process commands synchronously or asynchronously.

Supported options are `sync` and `async`.

Default: `sync`

### `event_processing`

Whether to process events synchronously or asynchronously.

Supported options are `sync` and `async`.

Default: `sync`

### `snapshot_threshold`

The threshold number of aggregate events after which a snapshot is created to
optimize performance.

Applies only when aggregates are event sourced.

Default: `10`

## Adapter Configuration

### `databases`
Expand All @@ -65,8 +178,7 @@ by the `default` key, and is used when you do not specify a database name when
accessing the domain.

The only other database defined by default is `memory`, which is the in-memory
stub database provider. You can name all other database definitions as
necessary.
stub database provider.

The persistence store defined here is then specified in the `provider` key of
aggregates and entities to assign them a specific database.
Expand All @@ -81,18 +193,67 @@ class User:
1. `sqlite` is the key of the database definition in the `[databases.sqlite]`
section.

### `cache`
Read more in [Adapters → Database](../adapters/database/index.md) section.

### `caches`

This section holds definitions for cache infrastructure.

```toml
[caches.default]
provider = "memory"

[caches.redis]
provider = "redis"
URI = "redis://127.0.0.1:6379/2"
TTL = 300
```

Default provider: `memory`

Read more in [Adapters → Cache](../adapters/cache/index.md) section.

### `broker`

This section holds configurations for message brokers.

```toml
[brokers.default]
provider = "memory"

[brokers.redis]
provider = "redis"
URI = "redis://127.0.0.1:6379/0"
IS_ASYNC = true
```

Default provider: `memory`

Read more in [Adapters → Broker](../adapters/broker/index.md) section.

### `event_store`

The event store that stores event and command messages is defined in this
section.

```toml
[event_store]
provider = "message_db"
database_uri = "postgresql://message_store@localhost:5433/message_store"
```

Note that there can only be only event store defined per domain.

Default provider: `memory`

Read more in [Adapters → Event Store](../adapters/eventstore/index.md) section.

## Custom Attributes

Custom attributes can be defined in toml under the `[custom]` section (or
`[tool.protean.custom]` if you are leveraging the `pyproject.toml` file).

Custom attributes are also made available on the domain object directly.
Custom attributes are also made available as domain attributes.

```toml hl_lines="5"
debug = true
Expand All @@ -111,3 +272,35 @@ Out[2]: 'bar'
In [3]: domain.FOO
Out[3]: 'bar'
```

## Multiple Environments

Most applications need more than one configuration. At the very least, there
should be separate configurations for production and for local development.
The `toml` configuration file can hold configurations for different
environments.

The current environment is gathered from an environment variable named
`PROTEAN_ENV`.

The string specified in `PROTEAN_ENV` is used as a qualifier in the
configuration.

```toml hl_lines="4 8"
[databases.default]
provider = "memory"

[staging.databases.default]
provider = "sqlite"
database_url = "sqlite:///test.db"

[prod.databases.default]
provider = "postgresql"
database_url = "postgresql://postgres:postgres@localhost:5432/postgres"
```

Protean has a default configuration with memory stubs that is overridden
by configurations in the `toml` file, which can further be over-ridden by an
environment-specific configuration, as seen above. There are two environment
specific settings above for databases - an `sqlite` db configuration for
`staging` and a `postgresql` db configuration for `prod`.
2 changes: 1 addition & 1 deletion docs/guides/domain-definition/fields/simple-fields.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ value is expected to be generated by the database at the time of persistence.
Cross-check with the specific adapter's documentation and your database
to confirm if the database supports this functionality.

- **`identity_strategy`**: The strategy to use to generate the identity value.
- **`identity_strategy`**: The strategy to use to generate an identity value.
If not provided, the strategy defined at the domain level is used.

- **`identity_function`**: A function that is used to generate the identity
Expand Down
2 changes: 1 addition & 1 deletion src/protean/adapters/email/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def __init__(self, domain):

def _initialize_email_providers(self):
"""Read config file and initialize email providers"""
configured_email_providers = self.domain.config["EMAIL_PROVIDERS"]
configured_email_providers = self.domain.config["email_providers"]
email_provider_objects = {}

if configured_email_providers and isinstance(configured_email_providers, dict):
Expand Down
Loading

0 comments on commit 7ab79a1

Please sign in to comment.