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

Update documentation for EFCore_WebApi example #2054

Merged
Changes from 6 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
85 changes: 64 additions & 21 deletions samples/WebAPI_EFCore/README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,47 @@
# Table of content
- [Web API and EF Core Example](#web-api-and-ef-core-example)
* [Enviroments](#enviroments)
* [Architecture](#architecture)
+ [Outbox](#outbox)
+ [GreetingsAPI](#greetingsapi)
+ [SalutationAnalytics](#salutationanalytics)
* [Build and Deploy](#build-and-deploy)
+ [Building](#building)
+ [Deploy](#deploy)
+ [Possible issues](#possible-issues)
- [Sqlite Database Read-Only Errors](#sqlite-database-read-only-errors)
- [Queue Creation and Dropped Messages](#queue-creation-and-dropped-messages)
- [Connection issue with the RabbitMQ](#connection-issue-with-the-rabbitmq)
- [Helpful documentation links](#helpful-documentation-links)
* [Tests](#tests)
# Web API and EF Core Example
This sample shows a typical scenario when using WebAPI and Brighter/Darker. It demonstrates both using Brigher and Darker to implement the API endpoints, and using a work queue to handle asynchronous work that results from handling the API call.

## Enviroments

*Development* - runs locally on your machine, uses Sqlite as a data store; uses RabbitMQ for messaging, can be launched invidvidually from the docker compose file; it represents a typical setup for development
*Development* - runs locally on your machine, uses Sqlite as a data store; uses RabbitMQ for messaging, can be launched invidvidually from the docker compose file; it represents a typical setup for development.

*Production* - runs in Docker, uses MySql as a data store; uses RabbitMQ for messaging; it emulates a possible production environment.

We provide launchSetting.json files for both, which allows you to run Production; you should launch MySQl and RabbitMQ from the docker compose file; useful for for debugging MySQL operations.
We provide launchSetting.json files for both, which allows you to run Production; you should launch MySQl and RabbitMQ from the docker compose file; useful for debugging MySQL operations.

In case you are using Command Line Interface for running the project, consider adding --launch-profile:

```sh
dotnet run --launch-profile Production -d
```
## Architecture

### Outbox
Brighter does have an [Outbox pattern support](https://paramore.readthedocs.io/en/latest/OutboxPattern.html). In case you are new to it, consider reading it before diving deeper.
### GreetingsAPI

We follow a ports and adapters archtectural style, dividing the app into the following modules:
We follow a _ports and adapters_ archtectural style, dividing the app into the following modules:

**GreetingsAdapters**: The adapters module, handles the primary adapter of HTTP requests and responses to the app
* **GreetingsAdapters**: The adapters module, handles the primary adapter of HTTP requests and responses to the app

**GreetingsPorts**: the ports module, handles requests from the primary adapter (HTTP) to the domain, and requests to secondary adapters. In a fuller app, the handlers for the primary adapter would correspond to our use case boundaries. The secondary port of the EntityGateway handles access to the DB via EF Core. We choose to treat EF Core as a port, not an adapter itself, here, as it wraps our underlying adapters for Sqlite or MySql.
* **GreetingsPorts**: the ports module, handles requests from the primary adapter (HTTP) to the domain, and requests to secondary adapters. In a fuller app, the handlers for the primary adapter would correspond to our use case boundaries. The secondary port of the EntityGateway handles access to the DB via EF Core. We choose to treat EF Core as a port, not an adapter itself, here, as it wraps our underlying adapters for Sqlite or MySql.

**GreetingsEntities**: the domain model (or application in ports & adapaters). In a fuller app, this would contain the logic that has a dependency on entity state.
* **GreetingsEntities**: the domain model (or application in ports & adapaters). In a fuller app, this would contain the logic that has a dependency on entity state.

We 'depend inwards' i.e. **GreetingsAdapters -> GreetingsPorts -> GreetingsEntities**

Expand All @@ -30,7 +51,7 @@ The assemblies migrations: **Greetings_MySqlMigrations** and **Greetings_SqliteM

This listens for a GreetingMade message and stores it. It demonstrates listening to a queue. It also demonstrates the use of scopes provided by Brighter's ServiceActivator, which work with EFCore. These support writing to an Outbox when this component raises a message in turn.

We don't listen to that message, and without any listeners the RMQ will drop the message we send, as it has no queues to give it to. We don't listen because we would just be repeating what we have shown here.
We don't listen to that message, and without any listeners the RabbitMQ will drop the message we send, as it has no queues to give it to. We don't listen because we would just be repeating what we have shown here.

We also add an Inbox here. The Inbox can be used to de-duplicate messages. In messaging the guarantee is 'at least once' if you use a technique such as an Outbox to ensure sending. This means we may receive a message twice. We either need, as in this case, to use an Inbox to de-duplicate, or we need to be idempotent such that receiving the message multiple times would result in the same outcome.

Expand All @@ -50,32 +71,54 @@ A common error is to change something, forget to run build.sh and use an old Doc

### Deploy

We provide a docker compose file to allow you to run a 'Production' environment or to startup RabbitMQ for production
We provide a docker compose file to allow you to run a 'Production' environment or to startup RabbitMQ for production:
```sh
docker compose up -d rabbitmq # will just start rabbitmq
```

-- docker compose up -d rabbitmq -- will just start rabbitmq
```sh
docker compose up -d mysql # will just start mysql
```

-- docker compose up -d mysql -- will just start mysql

and so on
and so on.

### Sqlite Database Read-Only Errors
### Possible issues
#### Sqlite Database Read-Only Errors

A Sqlite database will only have permissions for the process that created it. This can result in you receiving read-only errors between invocations of the sample. You either need to alter the permissions on your Db, or delete it between runs.

Maintainers, please don't check the Sqlite files into source control.

### Queue Creation and Dropped Messages
#### Queue Creation and Dropped Messages

Queues are created by consumers. This is because publishers don't know who consumes them, and thus don't create their queues. This means that if you run a producer, such as GreetingsWeb, and use tests.http to push in greetings, although a message will be published to RMQ, it won't have a queue to be delivered to and will be dropped, unless you have first run the SalutationAnalytics worker to create the queue.
Queues are created by consumers. This is because publishers don't know who consumes them, and thus don't create their queues. This means that if you run a producer, such as GreetingsWeb, and use tests.http to push in greetings, although a message will be published to RabbitMQ, it won't have a queue to be delivered to and will be dropped, unless you have first run the SalutationAnalytics worker to create the queue.

Generally, the rule of thumb is: start the consumer and *then* start the producer.

You can spot this by looking in the [RMQ Management console](http://localhost:1567) and noting that no queue is bound to the routing key in th exchange.
You can spot this by looking in the [RabbitMQ Management console](http://localhost:1567) and noting that no queue is bound to the routing key in th exchange.
You can use default credentials for the RabbitMQ Management console:
```sh
user :guest
passowrd: guest
```
#### Connection issue with the RabbitMQ
When running RabbitMQ from the docker compose file (without any additional network setup, etc) your RabbitMQ instance in docker will still be accessible by **localhost** as a host name. Consider this when running your application in the Production environment.
In Production the application by default will have:
```sh
amqp://guest:guest@rabbitmq:5672
```

as an Advanced Message Queuing Protocol (AMQP) connection string.
So one of the options will be replacing AMQP connection string with:
```sh
amqp://guest:guest@localhost:5672
```
In case you still struggle consider following this steps: [RabbitMQ Troubleshooting Networking](https://www.rabbitmq.com/troubleshooting-networking.html)
#### Helpful documentation links
* [Brighter technical documentation](https://paramore.readthedocs.io/en/latest/index.html)
* [Rabbit Message Queue (RMQ) documentation](https://www.rabbitmq.com/documentation.html)

## Tests

We provide a tests.http file (supported by both JetBrains Rider and VS Code with the REST Client plugin) to allow you to test operations.




We provide a tests.http file (supported by both JetBrains Rider and VS Code with the REST Client plugin) to allow you to test operations.