From caece9dd54584156308f86c80c92b6a32d40de1c Mon Sep 17 00:00:00 2001 From: Omelian Levkovych Date: Tue, 29 Mar 2022 22:53:11 +0300 Subject: [PATCH 1/7] fixed typo --- samples/WebAPI_EFCore/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/WebAPI_EFCore/README.md b/samples/WebAPI_EFCore/README.md index 1b3ae41945..60e8b39b39 100644 --- a/samples/WebAPI_EFCore/README.md +++ b/samples/WebAPI_EFCore/README.md @@ -3,11 +3,11 @@ This sample shows a typical scenario when using WebAPI and Brighter/Darker. It d ## 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. ## Architecture From 367bfbd9bd24ecbc51ec85fa4ec6ba7865c4dc60 Mon Sep 17 00:00:00 2001 From: Omelian Levkovych Date: Tue, 29 Mar 2022 22:53:52 +0300 Subject: [PATCH 2/7] prettify readme --- samples/WebAPI_EFCore/README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/samples/WebAPI_EFCore/README.md b/samples/WebAPI_EFCore/README.md index 60e8b39b39..a9b90e1c26 100644 --- a/samples/WebAPI_EFCore/README.md +++ b/samples/WebAPI_EFCore/README.md @@ -16,11 +16,11 @@ We provide launchSetting.json files for both, which allows you to run Production 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** @@ -50,13 +50,17 @@ 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 @@ -74,8 +78,4 @@ You can spot this by looking in the [RMQ Management console](http://localhost:15 ## 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. \ No newline at end of file From 0041b8f919bf38cd6f3ff0ca9f439f4d1c4ac511 Mon Sep 17 00:00:00 2001 From: Omelian Levkovych Date: Tue, 29 Mar 2022 23:16:06 +0300 Subject: [PATCH 3/7] renamed RMQ to RabbitMQ highlighted the adapters and ports pattern --- samples/WebAPI_EFCore/README.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/samples/WebAPI_EFCore/README.md b/samples/WebAPI_EFCore/README.md index a9b90e1c26..ab18a8bc5c 100644 --- a/samples/WebAPI_EFCore/README.md +++ b/samples/WebAPI_EFCore/README.md @@ -9,12 +9,16 @@ This sample shows a typical scenario when using WebAPI and Brighter/Darker. It d 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 ### 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 @@ -30,7 +34,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. @@ -70,12 +74,12 @@ Maintainers, please don't check the Sqlite files into source control. ### 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. ## 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. \ No newline at end of file +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. From b183f33b71a9955a5032b524225b9567b1468353 Mon Sep 17 00:00:00 2001 From: Omelian Levkovych Date: Tue, 29 Mar 2022 23:30:27 +0300 Subject: [PATCH 4/7] overcoming RMQ connection struggles --- samples/WebAPI_EFCore/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/samples/WebAPI_EFCore/README.md b/samples/WebAPI_EFCore/README.md index ab18a8bc5c..1f21d50054 100644 --- a/samples/WebAPI_EFCore/README.md +++ b/samples/WebAPI_EFCore/README.md @@ -79,6 +79,21 @@ Queues are created by consumers. This is because publishers don't know who consu Generally, the rule of thumb is: start the consumer and *then* start the producer. 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. + +you will not be able to connect to the RabbitMQ instaince in the docker container with ## Tests From 0be9dfa6f4ebd3bf05d23aeda19dbb96e1d72ba9 Mon Sep 17 00:00:00 2001 From: Omelian Levkovych Date: Tue, 29 Mar 2022 23:48:48 +0300 Subject: [PATCH 5/7] prettified links added more information on troubleshooting highlited the outbox pattern as a separate subtopic --- samples/WebAPI_EFCore/README.md | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/samples/WebAPI_EFCore/README.md b/samples/WebAPI_EFCore/README.md index 1f21d50054..d47cd9db91 100644 --- a/samples/WebAPI_EFCore/README.md +++ b/samples/WebAPI_EFCore/README.md @@ -15,7 +15,8 @@ In case you are using Command Line Interface for running the project, consider a 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: @@ -66,13 +67,14 @@ docker compose up -d mysql # will just start mysql 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 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. @@ -84,16 +86,22 @@ You can use default credentials for the RabbitMQ Management console: 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. +#### 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. - -you will not be able to connect to the RabbitMQ instaince in the docker container with + +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 From 557b1dff6dccaf1907e9fe3758a810045b4c3789 Mon Sep 17 00:00:00 2001 From: Omelian Levkovych Date: Tue, 29 Mar 2022 23:58:56 +0300 Subject: [PATCH 6/7] added table of content --- samples/WebAPI_EFCore/README.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/samples/WebAPI_EFCore/README.md b/samples/WebAPI_EFCore/README.md index d47cd9db91..fb8ceaedb7 100644 --- a/samples/WebAPI_EFCore/README.md +++ b/samples/WebAPI_EFCore/README.md @@ -1,3 +1,19 @@ +# 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. @@ -105,4 +121,4 @@ In case you still struggle consider following this steps: [RabbitMQ Troubleshoot ## 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. \ No newline at end of file From 85ee813e60914c5e66ddf2b7862e5534bcccd488 Mon Sep 17 00:00:00 2001 From: Omelian Levkovych Date: Wed, 30 Mar 2022 12:34:57 +0300 Subject: [PATCH 7/7] fixed typos and grammar --- samples/WebAPI_EFCore/README.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/samples/WebAPI_EFCore/README.md b/samples/WebAPI_EFCore/README.md index fb8ceaedb7..8989cd84e3 100644 --- a/samples/WebAPI_EFCore/README.md +++ b/samples/WebAPI_EFCore/README.md @@ -1,6 +1,6 @@ # Table of content - [Web API and EF Core Example](#web-api-and-ef-core-example) - * [Enviroments](#enviroments) + * [Environments](#environments) * [Architecture](#architecture) + [Outbox](#outbox) + [GreetingsAPI](#greetingsapi) @@ -15,11 +15,11 @@ - [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. +This sample shows a typical scenario when using WebAPI and Brighter/Darker. It demonstrates both using Brighter and Darker to implement the API endpoints, and using a work queue to handle asynchronous work that results from handling the API call. -## Enviroments +## Environments -*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 individually 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. @@ -35,17 +35,17 @@ dotnet run --launch-profile Production -d 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_ architectural 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 & adapters). In a fuller app, this would contain the logic that has a dependency on entity state. -We 'depend inwards' i.e. **GreetingsAdapters -> GreetingsPorts -> GreetingsEntities** +We 'depend on inwards' i.e. **GreetingsAdapters -> GreetingsPorts -> GreetingsEntities** -The assemblies migrations: **Greetings_MySqlMigrations** and **Greetings_SqliteMigrations** hold generated code to configure the Db. Consider this adapter layer code - the use of separate modules allows us to switch migration per enviroment. +The assemblies migrations: **Greetings_MySqlMigrations** and **Greetings_SqliteMigrations** hold generated code to configure the Db. Consider this adapter layer code - the use of separate modules allows us to switch migration per environment. ### SalutationAnalytics @@ -53,7 +53,7 @@ This listens for a GreetingMade message and stores it. It demonstrates listening 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. +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. ## Build and Deploy @@ -62,7 +62,7 @@ We also add an Inbox here. The Inbox can be used to de-duplicate messages. In me Use the build.sh file to: -- Build both GreetingsAdapters and GreetingsWatcher and publish it to the out directory. The Dockerfile assumes the app will be published here. +- Build both GreetingsAdapters and GreetingsWatcher and publish it to the /out directory. The Dockerfile assumes the app will be published here. - Build the Docker image from the Dockerfile for each. (Why not use a multi-stage Docker build? We can't do this as the projects here reference projects not NuGet packages for Brighter libraries and there are not in the Docker build context.) @@ -96,15 +96,15 @@ Queues are created by consumers. This is because publishers don't know who consu Generally, the rule of thumb is: start the consumer and *then* start the producer. -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 spot this by looking in the [RabbitMQ Management console](http://localhost:1567) and noting that no queue is bound to the routing key in the 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: +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 ``` @@ -114,7 +114,7 @@ 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) +In case you still struggle, consider following these 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)