Skip to content

Commit e5b03aa

Browse files
author
Sergio García Prado
authored
Merge pull request #125 from minos-framework/0.4.1
0.4.1
2 parents 03703cf + 0da7a02 commit e5b03aa

File tree

27 files changed

+381
-384
lines changed

27 files changed

+381
-384
lines changed

.github/workflows/minos-microservice-aggregate-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44
push:
55
branches:
66
- main
7+
- '*.*.x'
78
pull_request:
89
paths:
910
- 'packages/core/minos-microservice-aggregate/**'

.github/workflows/minos-microservice-common-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44
push:
55
branches:
66
- main
7+
- '*.*.x'
78
pull_request:
89
paths:
910
- 'packages/core/minos-microservice-common/**'

.github/workflows/minos-microservice-cqrs-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44
push:
55
branches:
66
- main
7+
- '*.*.x'
78
pull_request:
89
paths:
910
- 'packages/core/minos-microservice-cqrs/**'

.github/workflows/minos-microservice-networks-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44
push:
55
branches:
66
- main
7+
- '*.*.x'
78
pull_request:
89
paths:
910
- 'packages/core/minos-microservice-networks/**'

.github/workflows/minos-microservice-saga-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44
push:
55
branches:
66
- main
7+
- '*.*.x'
78
pull_request:
89
paths:
910
- 'packages/core/minos-microservice-saga/**'

README.md

Lines changed: 219 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,230 @@
1-
21
<p align="center">
32
<a href="http://minos.run" target="_blank"><img src="https://raw.githubusercontent.com/minos-framework/.github/main/images/logo.png" alt="Minos logo"></a>
43
</p>
54

65
# minos-python: The framework which helps you create reactive microservices in Python
7-
[![PyPI Latest Release](https://img.shields.io/pypi/v/minos-microservice-aggregate.svg?label=minos-microservice-aggregate)](https://pypi.org/project/minos-microservice-aggregate/)
8-
[![PyPI Latest Release](https://img.shields.io/pypi/v/minos-microservice-common.svg?label=minos-microservice-common)](https://pypi.org/project/minos-microservice-common/)
9-
[![PyPI Latest Release](https://img.shields.io/pypi/v/minos-microservice-cqrs.svg?label=minos-microservice-cqrs)](https://pypi.org/project/minos-microservice-cqrs/)
10-
[![PyPI Latest Release](https://img.shields.io/pypi/v/minos-microservice-networks.svg?label=minos-microservice-networks)](https://pypi.org/project/minos-microservice-networks/)
11-
[![PyPI Latest Release](https://img.shields.io/pypi/v/minos-microservice-saga.svg?label=minos-microservice-saga)](https://pypi.org/project/minos-microservice-saga/)
6+
7+
[![PyPI Latest Release](https://img.shields.io/pypi/v/minos-microservice-common.svg)](https://pypi.org/project/minos-microservice-common/)
128
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/minos-framework/minos-python/pages%20build%20and%20deployment?label=docs)](https://minos-framework.github.io/minos-python)
139
[![License](https://img.shields.io/github/license/minos-framework/minos-python.svg)](https://github.com/minos-framework/minos-python/blob/main/LICENSE)
1410
[![Coverage](https://codecov.io/github/minos-framework/minos-python/coverage.svg?branch=main)](https://codecov.io/gh/minos-framework/minos-python)
1511
[![Stack Overflow](https://img.shields.io/badge/Stack%20Overflow-Ask%20a%20question-green)](https://stackoverflow.com/questions/tagged/minos)
1612
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/minos-framework/community)
13+
[![Tokei](https://tokei.rs/b1/github/minos-framework/minos-python?category=code)](https://github.com/minos-framework/minos-python)
1714

1815
## Summary
1916

20-
Minos is a framework which helps you create [reactive](https://www.reactivemanifesto.org/) microservices in Python.
21-
Internally, it leverages Event Sourcing, CQRS and a message driven architecture to fulfil the commitments of an
22-
asynchronous environment.
17+
Minos is a framework which helps you create [reactive](https://www.reactivemanifesto.org/) microservices in Python. Internally, it leverages Event Sourcing, CQRS and a message driven architecture to fulfil the commitments of an asynchronous environment.
18+
19+
## Foundational Patterns
20+
21+
The `minos` framework is built strongly following the following set of patterns:
22+
23+
### Application architecture
24+
25+
* [Microservice architecture](https://microservices.io/patterns/microservices.html): Architect an application as a collection of loosely coupled services.
26+
27+
### Decomposition
28+
29+
* [Decompose by subdomain](https://microservices.io/patterns/decomposition/decompose-by-subdomain.html): Define services corresponding to Domain-Driven Design (DDD) subdomains
30+
* [Self-contained Service](https://microservices.io/patterns/decomposition/self-contained-service.html): Microservices can respond to a synchronous request without waiting for the response from any other service.
31+
32+
### Data management
33+
34+
* [Database per service](https://microservices.io/patterns/data/database-per-service.html): Keep each microservice's persistent data private to that service and accessible only via its API. A service's transactions only involve its database.
35+
* [Saga](https://microservices.io/patterns/data/saga.html): Transaction that spans multiple services.
36+
* [CQRS](https://microservices.io/patterns/data/cqrs.html): view database, which is a read-only replica that is designed to support queries that retrieves data from microservice. The application keeps the replica up to date by subscribing to Domain events published by the service that own the data.
37+
* [Domain event](https://microservices.io/patterns/data/domain-event.html): A service often needs to publish events when it updates its data. These events might be needed, for example, to update a CQRS view.
38+
* [Event Sourcing](https://microservices.io/patterns/data/event-sourcing.html): Event sourcing persists the state of a business entity such an Order or a Customer as a sequence of state-changing events. Whenever the state of a business entity changes, a new event is appended to the list of events. Since saving an event is a single operation, it is inherently atomic. The application reconstructs an entity's current state by replaying the events.
39+
40+
### Communication style
41+
42+
* [Messaging](https://microservices.io/patterns/communication-style/messaging.html): Services communicating by exchanging messages over messaging channels. (Apache Kafka is used in this case)
43+
44+
### External APIs
45+
46+
* [API gateway](https://microservices.io/patterns/apigateway.html): Single entry point for all clients. The API gateway proxy/route to the appropriate service.
47+
48+
### Service discovery
49+
50+
* [Service registry](https://microservices.io/patterns/service-registry.html): Database of services. A service registry might invoke a service instance's health check API to verify that it is able to handle requests
51+
* [Self Registration](https://microservices.io/patterns/self-registration.html): Each service instance register on startup and unregister on stop.
52+
53+
### Security
54+
55+
* [Access token](https://microservices.io/patterns/security/access-token.html): The API Gateway authenticates the request and passes an access token (e.g. JSON Web Token) that securely identifies the requestor in each request to the services. A service can include the access token in requests it makes to other services.
56+
57+
### Observability
58+
59+
* [Health Check API](https://microservices.io/patterns/observability/health-check-api.html): A service has a health check API endpoint (e.g. HTTP `/health`) that returns the health of the service.
60+
61+
## Installation
62+
63+
### Guided installation
64+
65+
The easiest way to use `minos` is with the help of the [`minos-cli`](https://github.com/minos-framework/minos-cli), which provides commands to setup both the project skeleton (configures containerization, databases, brokers, etc.) and the microservice skeleton (the base microservice structure, environment configuration, etc.)
66+
67+
### Manual installation
68+
69+
If you want to directly use `minos` without the command-line utility, the following command will install the needed packages:
70+
71+
```shell
72+
pip install \
73+
minos-microservice-aggregate \
74+
minos-microservice-common \
75+
minos-microservice-cqrs \
76+
minos-microservice-networks \
77+
minos-microservice-saga
78+
```
79+
80+
## Usage
81+
82+
This section includes a set of minimal examples of how-to-work with `minos`, so that anyone can get the gist of the framework.
83+
84+
### Create an Aggregate
85+
86+
Here is an example of the creation the `Foo` aggregate. In this case, it has two attributes, a `bar` being a `str`, and a `foobar` being an optional reference to the external `FooBar` aggregate, which it is assumed that it has a `something` attribute.
87+
88+
```python
89+
from __future__ import annotations
90+
from typing import Optional
91+
from minos.aggregate import Aggregate, AggregateRef, ModelRef
92+
93+
94+
class Foo(Aggregate):
95+
"""Foo Aggregate class."""
96+
97+
bar: str
98+
foobar: Optional[ModelRef[FooBar]]
99+
100+
101+
class FooBar(AggregateRef):
102+
"""FooBar AggregateRef clas."""
103+
104+
something: str
105+
```
106+
107+
### Expose a Command
108+
109+
Here is an example of the definition of a command to create `Foo` instances. To do that, it is necessary to define a `CommandService` that contains the handling function. It will handle both the broker messages sent to the `"CreateFoo"` topic and the rest calls to the `"/foos"` path with the `"POST"` method. In this case, the handling function unpacks the `Request`'s content and then calls the `create` method from the `Aggregate`, which stores the `Foo` instance following an event-driven strategy (it also publishes the `"FooCreated"` event). Finally, a `Response` is returned to be handled by the external caller (another microservice or the API-gateway).
110+
111+
```python
112+
from minos.cqrs import CommandService
113+
from minos.networks import enroute, Request, Response
114+
115+
116+
class FooCommandService(CommandService):
117+
"""Foo Command Service class."""
118+
119+
@enroute.broker.command("CreateFoo")
120+
@enroute.rest.command("/foos", "POST")
121+
async def create_foo(self, request: Request) -> Response:
122+
"""Create a new Foo.
123+
124+
:param request: The ``Request`` that contains the ``bar`` attribute.
125+
:return: A ``Response`` containing identifier of the already created instance.
126+
"""
127+
content = await request.content()
128+
bar = content["bar"]
129+
130+
foo = await Foo.create(bar)
131+
132+
return Response({"uuid": foo.uuid})
133+
```
134+
135+
### Subscribe to an Event and Expose a Query
136+
137+
Here is an example of the event and query handling. In this case, it must be defined on a `QueryService` class. In this case a `"FooCreated"` event is handled (and only a `print` of the content is performed). The event contents typically contains instances of `AggregateDiff` type, which is referred to the difference respect to the previously stored instance. The exposed query is connected to the calls that come from the `"/foos/example"` path and `"GET"` method and a naive string is returned.
138+
139+
*Disclaimer*: A real `QueryService` implementation must populate a query-oriented database based on the events to which is subscribed to, and expose queries performed over that query-oriented database.
140+
141+
```python
142+
from minos.cqrs import QueryService
143+
from minos.networks import enroute, Request, Response
144+
145+
146+
class FooQueryService(QueryService):
147+
"""Foo Query Service class."""
148+
149+
@enroute.broker.event("FooCreated")
150+
async def foo_created(self, request: Request) -> None:
151+
"""Handle the "FooCreated" event.
152+
153+
:param request: The ``Request`` that contains the ``bar`` attribute.
154+
:return: This method does not return anything.
155+
"""
156+
diff = await request.content()
157+
print(f"A Foo was created: {diff}")
158+
159+
@enroute.rest.query("/foos/example", "GET")
160+
async def example(self, request: Request) -> Response:
161+
"""Handle the example query.
162+
163+
:param request: The ``Request`` that contains the necessary information.
164+
:return: A ``Response`` instance.
165+
"""
166+
return Response("This is an example response!")
167+
```
168+
169+
### Interact with another Microservice
170+
171+
Here is an example of the interaction between two microservices through a SAGA pattern. In this case, the interaction starts with a call to the `"/foo/add-foobar"` path and the `"POST"` method, which performs a `SagaManager` run over the `ADD_FOOBAR_SAGA` saga. This saga has two steps, one remote that executes the `"CreateFooBar"` command (possibly defined on the supposed `"foobar"` microservice), and a local step that is executed on this microservice. The `CreateFooBarDTO` defines the structure of the request to be sent when the `"CreateFooBar"` command is executed.
172+
173+
```python
174+
from minos.common import ModelType
175+
from minos.cqrs import CommandService
176+
from minos.networks import enroute, Request
177+
from minos.saga import Saga, SagaContext, SagaRequest, SagaResponse
178+
179+
CreateFooBarDTO = ModelType.build("AnotherDTO", {"number": int, "text": str})
180+
181+
182+
def _create_foobar(context: SagaContext) -> SagaRequest:
183+
something = context["something"]
184+
content = CreateFooBarDTO(56, something)
185+
return SagaRequest("CreateFooBar", content)
186+
187+
188+
async def _success_foobar(context: SagaContext, response: SagaResponse) -> SagaContext:
189+
context["foobar_uuid"] = await response.content()
190+
return context
191+
192+
193+
async def _error_foobar(context: SagaContext, response: SagaResponse) -> SagaContext:
194+
raise ValueError("The foobar could not be created!")
195+
196+
197+
async def _update_foo(context: SagaContext) -> None:
198+
foo = await Foo.get(context["uuid"])
199+
foo.foobar = context["foobar_uuid"]
200+
await foo.save()
201+
202+
203+
ADD_FOOBAR_SAGA = (
204+
Saga()
205+
.remote_step().on_execute(_create_foobar).on_success(_success_foobar).on_error(_error_foobar)
206+
.local_step().on_execute(_update_foo)
207+
.commit()
208+
)
209+
210+
211+
class FooCommandService(CommandService):
212+
"""Foo Command Service class."""
213+
214+
@enroute.rest.command("/foo/add-foobar", "POST")
215+
async def update_foo(self, request: Request) -> None:
216+
"""Run a saga example.
217+
218+
:param request: The ``Request`` that contains the initial saga's context.
219+
:return: This method does not return anything.
220+
"""
221+
content = await request.content()
222+
223+
await self.saga_manager.run(
224+
ADD_FOOBAR_SAGA, {"uuid": content["uuid"], "something": content["something"]}
225+
)
226+
227+
```
23228

24229
## Documentation
25230

@@ -41,17 +246,20 @@ The core packages provide the base implementation of the framework.
41246

42247
### Plugins
43248

44-
The plugin packages provide connectors to external technologies like brokers, discovery services, databases, serializers and so on.
249+
The plugin packages provide connectors to external technologies like brokers, discovery services, databases, serializers and so on.
250+
251+
* Coming soon...
45252

46253
## Source Code
47254

48-
The source code of this project is hosted at [GitHub](https://github.com/minos-framework/minos-python).
255+
The source code of this project is hosted at [GitHub](https://github.com/minos-framework/minos-python).
49256

50257
## Getting Help
51258

52259
For usage questions, the best place to go to is [StackOverflow](https://stackoverflow.com/questions/tagged/minos).
53260

54261
## Discussion and Development
262+
55263
Most development discussions take place over the [GitHub Issues](https://github.com/minos-framework/minos-python/issues). In addition, a [Gitter channel](https://gitter.im/minos-framework/community) is available for development-related questions.
56264

57265
## How to contribute

packages/core/minos-microservice-aggregate/HISTORY.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,7 @@
5050

5151
* Be compatible with `minos-microservice-common~=0.4.0`.
5252
* Be compatible with `minos-microservice-networks~=0.4.0`.
53+
54+
## 0.4.1 (2022-01-31)
55+
56+
* Update `README.md`.

0 commit comments

Comments
 (0)