Browse Source

Merge pull request #21456 from abpframework/microservice-tutorial

Microservice development tutorial added
pull/21461/head
Engincan VESKE 2 years ago
committed by GitHub
parent
commit
638521964b
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 37
      docs/en/docs-nav.json
  2. BIN
      docs/en/get-started/images/abp-studio-microservice-solution-runner-docker-dependencies.png
  3. 32
      docs/en/get-started/microservice.md
  4. 5
      docs/en/tutorials/index.md
  5. BIN
      docs/en/tutorials/microservice/images/abp-studio-abp-suite-inside.png
  6. BIN
      docs/en/tutorials/microservice/images/abp-studio-add-entity-framework-core-migration.png
  7. BIN
      docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-command-2.png
  8. BIN
      docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-command.png
  9. BIN
      docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-2.png
  10. BIN
      docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-additional-options-step.png
  11. BIN
      docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-database-step.png
  12. BIN
      docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-integration-step.png
  13. BIN
      docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog.png
  14. BIN
      docs/en/tutorials/microservice/images/abp-studio-browse-catalog-service-2.png
  15. BIN
      docs/en/tutorials/microservice/images/abp-studio-browse-catalog-service.png
  16. BIN
      docs/en/tutorials/microservice/images/abp-studio-browse-cloud-crm-products.png
  17. BIN
      docs/en/tutorials/microservice/images/abp-studio-browse-ordering-service.png
  18. BIN
      docs/en/tutorials/microservice/images/abp-studio-browser-catalog-service-swagger-ui.png
  19. BIN
      docs/en/tutorials/microservice/images/abp-studio-catalog-service-build-and-start.png
  20. BIN
      docs/en/tutorials/microservice/images/abp-studio-entity-framework-core-add-migration-order.png
  21. BIN
      docs/en/tutorials/microservice/images/abp-studio-generate-proxy-2.png
  22. BIN
      docs/en/tutorials/microservice/images/abp-studio-generate-proxy-window-ordering-module.png
  23. BIN
      docs/en/tutorials/microservice/images/abp-studio-generate-proxy-window.png
  24. BIN
      docs/en/tutorials/microservice/images/abp-studio-generate-proxy.png
  25. BIN
      docs/en/tutorials/microservice/images/abp-studio-new-catalog-service-in-solution-explorer.png
  26. BIN
      docs/en/tutorials/microservice/images/abp-studio-open-abp-suite-select-module.png
  27. BIN
      docs/en/tutorials/microservice/images/abp-studio-open-abp-suite.png
  28. BIN
      docs/en/tutorials/microservice/images/abp-studio-open-with-visual-studio.png
  29. BIN
      docs/en/tutorials/microservice/images/abp-studio-run-build-and-start-all.png
  30. BIN
      docs/en/tutorials/microservice/images/abp-studio-run-build-start.png
  31. BIN
      docs/en/tutorials/microservice/images/abp-studio-run-solution.png
  32. BIN
      docs/en/tutorials/microservice/images/abp-studio-solution-explorer-initial-cloud-crm-microservice-solution.png
  33. BIN
      docs/en/tutorials/microservice/images/abp-studio-solution-explorer-ordering-microservice.png
  34. BIN
      docs/en/tutorials/microservice/images/abp-studio-solution-runner-play-all.png
  35. BIN
      docs/en/tutorials/microservice/images/abp-suite-product-generated.png
  36. BIN
      docs/en/tutorials/microservice/images/abp-suite-product-generating.png
  37. BIN
      docs/en/tutorials/microservice/images/abp-suite-product-info.png
  38. BIN
      docs/en/tutorials/microservice/images/abp-suite-product-properties.png
  39. BIN
      docs/en/tutorials/microservice/images/add-catalog-service-contracts-reference.png
  40. BIN
      docs/en/tutorials/microservice/images/add-package-reference-ordering-service.png
  41. BIN
      docs/en/tutorials/microservice/images/catalog-service-dependency.png
  42. BIN
      docs/en/tutorials/microservice/images/create-order.png
  43. BIN
      docs/en/tutorials/microservice/images/generate-catalog-service-proxy.png
  44. BIN
      docs/en/tutorials/microservice/images/generate-proxy-catalog-service.png
  45. BIN
      docs/en/tutorials/microservice/images/import-module-dialog.png
  46. BIN
      docs/en/tutorials/microservice/images/import-module.png
  47. BIN
      docs/en/tutorials/microservice/images/install-module-dialog.png
  48. BIN
      docs/en/tutorials/microservice/images/ordering-service-order-swagger-ui.png
  49. BIN
      docs/en/tutorials/microservice/images/ordering-service-swagger-ui.png
  50. BIN
      docs/en/tutorials/microservice/images/orders.png
  51. BIN
      docs/en/tutorials/microservice/images/sql-server-management-studio-databases-2.png
  52. BIN
      docs/en/tutorials/microservice/images/sql-server-management-studio-databases.png
  53. BIN
      docs/en/tutorials/microservice/images/sql-server-management-studio-login-screen.png
  54. BIN
      docs/en/tutorials/microservice/images/sql-server-management-studio-products.png
  55. BIN
      docs/en/tutorials/microservice/images/sql-server-orders-database-table-records.png
  56. BIN
      docs/en/tutorials/microservice/images/visual-studio-new-migration-class.png
  57. BIN
      docs/en/tutorials/microservice/images/visual-studio-solution-explorer-catalog-service.png
  58. BIN
      docs/en/tutorials/microservice/images/vs-ordering-contracts.png
  59. BIN
      docs/en/tutorials/microservice/images/vs-ordering-entity.png
  60. BIN
      docs/en/tutorials/microservice/images/web-orders-page-with-product-name.png
  61. BIN
      docs/en/tutorials/microservice/images/web-orders-page.png
  62. 36
      docs/en/tutorials/microservice/index.md
  63. 40
      docs/en/tutorials/microservice/part-01.md
  64. 123
      docs/en/tutorials/microservice/part-02.md
  65. 126
      docs/en/tutorials/microservice/part-03.md
  66. 97
      docs/en/tutorials/microservice/part-04.md
  67. 430
      docs/en/tutorials/microservice/part-05.md
  68. 311
      docs/en/tutorials/microservice/part-06.md
  69. 231
      docs/en/tutorials/microservice/part-07.md
  70. 2
      docs/en/tutorials/modular-crm/part-02.md

37
docs/en/docs-nav.json

@ -156,6 +156,43 @@
}
]
},
{
"text": "Microservice Solution",
"items": [
{
"text": "Overview",
"path": "tutorials/microservice/index.md"
},
{
"text": "1: Creating the initial solution",
"path": "tutorials/microservice/part-01.md"
},
{
"text": "2: Creating the initial Catalog service",
"path": "tutorials/microservice/part-02.md"
},
{
"text": "3: Building the Catalog service",
"path": "tutorials/microservice/part-03.md"
},
{
"text": "4: Creating the initial Ordering service",
"path": "tutorials/microservice/part-04.md"
},
{
"text": "5: Building the Ordering service",
"path": "tutorials/microservice/part-05.md"
},
{
"text": "6: Integrating the services: HTTP API Calls",
"path": "tutorials/microservice/part-06.md"
},
{
"text": "7: Integrating the services: Using Distributed Events",
"path": "tutorials/microservice/part-07.md"
}
]
},
{
"text": "Community Articles",
"path": "https://abp.io/community"

BIN
docs/en/get-started/images/abp-studio-microservice-solution-runner-docker-dependencies.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

32
docs/en/get-started/microservice.md

@ -73,11 +73,11 @@ Click the Next button to see *Additional Options* selection:
If you unchecked the *Kubernetes Configuration* option, the solution will not include the Kubernetes configuration files which include the Helm charts and other Kubernetes related files. You can also specify *Social Logins*; if you uncheck this option, the solution will not be configured for social login. Lastly, you can specify the *Include Tests* option to include the test projects in the solution.
Now, we are ready to allow ABP Studio to create our solution. Just click the *Create* button and let the ABP Studio do the rest for you. After clicking the Create button, the dialog is closed and your solution is loaded into ABP Studio:
Now, we are ready to allow ABP Studio to create our solution. Just click the *Create* button and let the ABP Studio do the rest for you. After clicking the *Create* button, the dialog is closed and your solution is loaded into ABP Studio:
![abp-studio-created-new-microservice-solution](images/abp-studio-created-new-microservice-solution.png)
You can explore the solution, but you need to wait for background tasks to be completed before running any application in the solution (it can take up to a few minutes to set up all).
You can explore the solution, but you need to **wait for background tasks to be completed** before running any application in the solution (it can take up to a few minutes to set up all).
> The solution structure can be different in your case based on the options you've selected.
@ -123,9 +123,7 @@ In the *Solution Runner* section (on the left side) you can see all the runnable
![abp-studio-microservice-solution-runner-applications](images/abp-studio-microservice-solution-runner-applications.png)
> All the leaf items in the *Solution Runner* is called as an *Application* as they are executable applications.
> For a faster start process, first start the *Docker-Dependencies*, then you can start all applications.
> A leaf item in the *Solution Runner* is called as an *Application* as it is an executable application.
As shown in the figure above, the executable applications are grouped into folders like `apps`, `gateways`, `infrastructure`, and `services`. You can start/stop them all, a group (folder) of them, or one by one.
@ -135,16 +133,14 @@ Before running the applications, it is good to be sure that all applications are
> *Solution Runner* doesn't build an application before running it. That provides a great performance gain because most of the time you will work on one or a few services and you don't need to build all of the other applications in every run. However, if you want to build before running, you can right-click an item in the *Solution Runner* tree and select *Run* -> *Build & Start* command.
It will take some time to build all. Once all is done, you can start the system.
You can click the *Play* button on the root item in Solution Runner to start all the applications. Or you can start `Docker-Dependencies` first, so the database and other infrastructure services get ready before the other applications:
![abp-studio-microservice-solution-runner-docker-dependencies](images/abp-studio-microservice-solution-runner-docker-dependencies.png)
It will take some time to build all. Once all is done, you can start the system. You can click the *Play* button on the root item in Solution Runner to start all the applications.
> **About the Docker Containers**
>
> Docker will fetch the docker images before starting the containers in your first run (if they were not fetched before) and that process may take a few minutes depending on your internet connection speed. So, please wait for it to completely start. If the process takes more time than you expect, you can right-click on `Docker-Dependencies` and select the *Logs* command to see what's happening.
Once `Docker-Dependencies` is ready, you can click the *Play* button on the root item in Solution Runner to start all the applications.
> **About Failing Services on Startup**
>
> Some applications/services may fail on the first run. That may be because of service and database dependencies were not satisfied and an error occurs on the application startup. ABP Studio automatically restarts failing services until it is successfully started. Being completely ready for such a distributed solution may take a while, but it will be eventually started.
Once all the applications are ready, you can right-click the `Web` application and select the *Browse* command:
@ -222,6 +218,8 @@ Once the solution is ready in Kubernetes, you can open a browser and visit the f
![abp-studio-microservice-web-application-home-page](images/abp-studio-microservice-web-application-home-page.png)
> We could use `cloudcrm-local-web` as the host name since ABP Studio has added an entry to the host file for us.
Click the *Login* link in the application UI, it will redirect you to the *Authentication Server* application, enter `admin` as username and `1q2w3E*` as password to login to the application.
> The services run independently from each other and perform some initial data seed logic on their startups. So, they may fail in their first run. In that case, Kubernetes will re-start them. So, it may initially get some time to make the solution fully ready and working.
@ -248,11 +246,11 @@ Clicking the *Connect* button will start a process that establishes the VPN conn
![abp-studio-microservice-kubernetes-services](images/abp-studio-microservice-kubernetes-services.png)
Now, you can access all the services inside the Kubernetes cluster, including the services those are not exposes out of the cluster. You can use the service name as DNS. For example, you can directly visit `http://cloudcrm-local-identity` in your Browser. You can also right-click to a service or application and select the Browse command to open it's UI in the built-in browser of ABP Studio:
Now, you can access all the services inside the Kubernetes cluster, including the services those are not exposed out of the cluster. You can use the service name as DNS. For example, you can directly visit `http://cloudcrm-local-identity` in your Browser. You can also right-click to a service or application and select the Browse command to open it's UI in the built-in browser of ABP Studio:
![abp-studio-microservice-kubernetes-services-browse](images/abp-studio-microservice-kubernetes-services-browse.png)
You can even use the other services (e.g. SQL Server or RabbitMQ) from your local computer (even if they were not exposed out of cluster) with their service names. `sa` password for the SQL server is `myPassw@rd` by default, you can use your SQL Server management studio to connect to it and see the databases:
You can even use the other services (e.g. SQL Server or RabbitMQ) from your local computer (even if they were not exposed out of cluster) with their service names. `sa` password for the SQL server is `myPassw@rd` by default, you can use your SQL Server management studio to connect to it and see the databases (*Server name* is `cloudcrm-local-sqlserver`):
![abp-studio-microservice-sql-server-connection](images/abp-studio-microservice-sql-server-connection.png)
@ -266,6 +264,8 @@ When you connect to Kubernetes, ABP Studio automatically connects to the applica
In this way, you can easily track HTTP requests, distributed events, exceptions, logs and other details of your applications.
> If you want to browse a web application in the integrated browser of ABP Studio, right-click to a service in the *Kubernetes* tab of the *Kubernetes* panel and select the *Browse* command.
## Kubernetes Integration: Intercepting Services
The next step is to intercept a service to forward the traffic (coming to that service) to your local computer, so you can run the same service in your local computer to test, debug and develop it. This is the way of connecting two environments (your local machine and the Kubernetes cluster) to develop your services integrated to Kubernetes.
@ -306,3 +306,7 @@ To re-deploy a service to Kubernetes, right-click the service and select *Comman
![abp-studio-microservice-kubernetes-redeploy](images/abp-studio-microservice-kubernetes-redeploy.png)
ABP Studio will re-build the Docker image and re-install it using the related Helm chart.
## See Also
* [Microservice Development Tutorial](../tutorials/microservice/index.md)

5
docs/en/tutorials/index.md

@ -3,5 +3,6 @@
The following introductory tutorials explain how to build applications based on the ABP platform:
* [TODO Application](todo/index.md): This is a single-part, quick-start tutorial to build a simple application with ABP. Start with this tutorial if you want to quickly understand how ABP works.
* [Book Store Application](book-store/index.md): This is a multi-part, complete tutorial to build a bookstore application with ABP. Start with this tutorial if you want to create a layered solution with ABP and apply DDD best practices.
* [Modular Monolith Application](modular-crm/index.md): This is a multi-part tutorial that demonstrates how to create application modules, compose and communicate them to build a monolith modular web application.
* [Book Store Application](book-store/index.md): A complete tutorial to build a bookstore application with ABP. Start with this tutorial if you want to create a layered solution with ABP and apply DDD best practices.
* [Modular Monolith Application](modular-crm/index.md): Demonstrates how to create application modules, compose and communicate them to build a monolith modular web application.
* [Microservice Solution](microservice/index.md): Explains how to build microservice solutions using ABP.

BIN
docs/en/tutorials/microservice/images/abp-studio-abp-suite-inside.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-add-entity-framework-core-migration.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-command-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-command.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-additional-options-step.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-database-step.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-integration-step.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-browse-catalog-service-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-browse-catalog-service.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-browse-cloud-crm-products.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-browse-ordering-service.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-browser-catalog-service-swagger-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-catalog-service-build-and-start.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-entity-framework-core-add-migration-order.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-generate-proxy-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-generate-proxy-window-ordering-module.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-generate-proxy-window.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-generate-proxy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-new-catalog-service-in-solution-explorer.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-open-abp-suite-select-module.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-open-abp-suite.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-open-with-visual-studio.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-run-build-and-start-all.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-run-build-start.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-run-solution.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-solution-explorer-initial-cloud-crm-microservice-solution.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-solution-explorer-ordering-microservice.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

BIN
docs/en/tutorials/microservice/images/abp-studio-solution-runner-play-all.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
docs/en/tutorials/microservice/images/abp-suite-product-generated.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
docs/en/tutorials/microservice/images/abp-suite-product-generating.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

BIN
docs/en/tutorials/microservice/images/abp-suite-product-info.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
docs/en/tutorials/microservice/images/abp-suite-product-properties.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

BIN
docs/en/tutorials/microservice/images/add-catalog-service-contracts-reference.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
docs/en/tutorials/microservice/images/add-package-reference-ordering-service.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
docs/en/tutorials/microservice/images/catalog-service-dependency.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

BIN
docs/en/tutorials/microservice/images/create-order.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

BIN
docs/en/tutorials/microservice/images/generate-catalog-service-proxy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
docs/en/tutorials/microservice/images/generate-proxy-catalog-service.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
docs/en/tutorials/microservice/images/import-module-dialog.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
docs/en/tutorials/microservice/images/import-module.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
docs/en/tutorials/microservice/images/install-module-dialog.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
docs/en/tutorials/microservice/images/ordering-service-order-swagger-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
docs/en/tutorials/microservice/images/ordering-service-swagger-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

BIN
docs/en/tutorials/microservice/images/orders.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
docs/en/tutorials/microservice/images/sql-server-management-studio-databases-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
docs/en/tutorials/microservice/images/sql-server-management-studio-databases.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

BIN
docs/en/tutorials/microservice/images/sql-server-management-studio-login-screen.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

BIN
docs/en/tutorials/microservice/images/sql-server-management-studio-products.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

BIN
docs/en/tutorials/microservice/images/sql-server-orders-database-table-records.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
docs/en/tutorials/microservice/images/visual-studio-new-migration-class.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
docs/en/tutorials/microservice/images/visual-studio-solution-explorer-catalog-service.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
docs/en/tutorials/microservice/images/vs-ordering-contracts.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
docs/en/tutorials/microservice/images/vs-ordering-entity.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
docs/en/tutorials/microservice/images/web-orders-page-with-product-name.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
docs/en/tutorials/microservice/images/web-orders-page.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

36
docs/en/tutorials/microservice/index.md

@ -1,3 +1,37 @@
# Microservice Development Tutorial
This tutorial is work in progress. Please check later. You can check here to [see the draft tutorial](https://github.com/abpframework/abp/blob/microservice-tutorial/docs/en/tutorials/microservice/index.md).
````json
//[doc-nav]
{
"Next": {
"Name": "Creating the initial solution",
"Path": "tutorials/microservice/part-01"
}
}
````
> This tutorial is suitable for those who have an ABP Business or a higher [license](https://abp.io/pricing).
ABP is designed to be a powerful platform to build microservice solutions. It provides a [microservice solution template](../../solution-templates/microservice/index.md) to easily start a sophisticated microservice solution. All of the [pre-built application modules](../../modules/index.md) are microservice compatible and the core framework fully [supports and simplifies](../../framework/architecture/microservices/index.md) distributed application development.
In this tutorial, you will learn how to start a new microservice solution, create services and communicate between them. You will also learn to use these services from a web application through an API gateway and automatically generate CRUD pages using the [ABP Suite](../../suite/index.md) tool.
## Tutorial Outline
This tutorial is organized as the following parts:
* [Part 01: Creating the initial solution](part-01.md)
* [Part 02: Creating the initial Catalog microservice](part-02.md)
* [Part 03: Building the Catalog microservice](part-03.md)
* [Part 04: Creating the initial Ordering service](part-04.md)
* [Part 05: Building the Ordering service](part-05.md)
* [Part 06: Integrating the services: HTTP API Calls](part-06.md)
* [Part 07: Integrating the services: Using Distributed Events](part-07.md)
## Download the Source Code
After logging in to the ABP website, you can download the source code from [here](https://abp.io/api/download/samples/cloud-crm-mvc-ef).
## See Also
* [Microservice solution template](../../solution-templates/microservice/index.md)

40
docs/en/tutorials/microservice/part-01.md

@ -0,0 +1,40 @@
# Microservice Tutorial Part 01: Creating the Initial Solution
````json
//[doc-nav]
{
"Next": {
"Name": "Creating the initial Catalog service",
"Path": "tutorials/microservice/part-02"
}
}
````
Follow the *[Get Started](../../get-started/microservice.md)* guide to create a new layered web application with the following configurations:
* **Solution name**: `CloudCrm`
* **Database Provider**: Entity Framework Core
* **Database Management System**: SQL Server
* **UI Framework**: MVC / Razor Pages
* **Mobile framework**: None
* **Public website**: Selected
You can select the other options based on your preference.
> **Please complete the *[Get Started](../../get-started/layered-web-application.md)* guide and run the web application before going further.** You can skip the sections after the *Running the Solution* section, if you don't prefer to complete all.
The initial solution structure should be like the following in ABP Studio's *[Solution Explorer](../../studio/solution-explorer.md)*:
![abp-studio-solution-explorer-initial-cloud-crm-microservice-solution](images/abp-studio-solution-explorer-initial-cloud-crm-microservice-solution.png)
> ABP Studio will perform a few additional steps after creating your solution. **Please wait until all the background tasks are completed** before going further.
Initially you see three folders (`apps`, `gateways` and `services`) and ~10 ABP Studio modules (depends on your preferences while creating the solution) under the `CloudCrm` ABP Studio solution. Some of these modules represent microservices, some of them represent web applications and some others represent API gateways in our system.
> An **ABP Studio module** is typically a .NET solution and an **ABP Studio solution** is an umbrella concept for multiple .NET Solutions (see the *[Concepts](../../studio/concepts.md)* document for more).
You can see the *[Microservice Solution Template](../../solution-templates/microservice/index.md)* document later if you want to understand the initial solution structure with all its details. However, it is not needed to follow this tutorial.
## Summary
In this part, you've created the initial microservice solution, which already contains a few infrastructure services. We will create our first business service in the [next part](part-02.md).

123
docs/en/tutorials/microservice/part-02.md

@ -0,0 +1,123 @@
# Microservice Tutorial Part 02: Creating the initial Catalog service
````json
//[doc-nav]
{
"Previous": {
"Name": "Creating the initial solution",
"Path": "tutorials/microservice/part-01"
},
"Next": {
"Name": "Building the Catalog service",
"Path": "tutorials/microservice/part-03"
}
}
````
In this tutorial, you will create a new Catalog service and integrate it to the solution.
## Creating the Catalog Service
Right-click the `services` folder in the *Solution Explorer* panel, select the *Add* -> *New Module* -> *Microservice* command:
![abp-studio-add-new-microservice-command](images/abp-studio-add-new-microservice-command.png)
This command opens a new dialog to define the properties of the new microservice. You can use the following values to create a new microservice named `CatalogService`:
![abp-studio-add-new-microservice-dialog](images/abp-studio-add-new-microservice-dialog.png)
When you click the *Next* button, you are redirected to the database provider selection step.
### Selecting the Database Type
Here, you can select the database provider to be used by the new microservice:
![abp-studio-add-new-microservice-dialog-database-step](images/abp-studio-add-new-microservice-dialog-database-step.png)
Select *Entity Framework Core* option and proceed the *Next* step.
### Integrating to the Solution
In this step, we can select the options for integrating the new microservice to the rest of the solution components:
![abp-studio-add-new-microservice-dialog-integration-step](images/abp-studio-add-new-microservice-dialog-integration-step.png)
ABP Studio intelligently selects the right values for you, but you should still check them carefully since they directly affect what we will do in the next parts of this tutorial.
**Ensure the options are configured the same as in the preceding figure**, and click the *Next* button.
### Additional Options
![abp-studio-add-new-microservice-dialog-additional-options-step](images/abp-studio-add-new-microservice-dialog-additional-options-step.png)
In this step, you can select additional options for the new microservice. You can leave them as default and click the *Create* button.
That's all, ABP Studio creates the new microservice and arranges all the integration and configuration for you.
## Exploring the New Catalog Microservice
In this section, we will investigate the new microservice in overall.
### Understanding the Packages of The Service
The new microservice is added under the `services` folder in the `CloudCrm` ABP Studio solution:
![abp-studio-new-catalog-service-in-solution-explorer](images/abp-studio-new-catalog-service-in-solution-explorer.png)
The new microservice has its own separate .NET solution that includes three packages (.NET projects):
* `CloudCrm.CatalogService` is the main project that you will implement your service. It typically contains your [entities](../../framework/architecture/domain-driven-design/entities.md), [repositories](../../framework/architecture/domain-driven-design/repositories.md), [application services](../../framework/architecture/domain-driven-design/application-services.md), API controllers, etc.
* `CloudCrm.CatalogService.Contracts` project can be shared with the other services and applications. It typically contains interfaces of your [application services](../../framework/architecture/domain-driven-design/application-services.md), [data transfer objects](../../framework/architecture/domain-driven-design/data-transfer-objects.md), and some other types you may want to share with the clients of this microservice.
* `CloudCrm.CatalogService.Tests` is for building your unit and integration tests for this microservice.
### Opening the Service in an IDE
You can open the new microservice in your favorite IDE for development. As a shortcut, you can right-click it in ABP Studio, select the *Open with* -> *Visual Studio* command for example:
![abp-studio-open-with-visual-studio](images/abp-studio-open-with-visual-studio.png)
Here is the `CloudCrm.CatalogService` .NET solution in Visual Studio:
![visual-studio-solution-explorer-catalog-service](images/visual-studio-solution-explorer-catalog-service.png)
### Running the New Service
You can run the solution using ABP Studio's *Solution Runner*. It will also run the new Catalog service as a part of the solution.
> Before running the solution, **ensure that all the applications are built**. If you are not sure, right-click the root item (`CloudCrm`) in the *Solution Explorer* panel and select the *Build* -> *Graph Build* command.
Click the *Play* button near to the solution root:
![abp-studio-solution-runner-play-all](images/abp-studio-solution-runner-play-all.png)
### Browsing the Catalog Service
Once all of the applications have started, right-click the Catalog service and select the *Browse* command:
![abp-studio-browse-catalog-service](images/abp-studio-browse-catalog-service.png)
It will open the built-in browser and you will see the Swagger UI for the Catalog service:
![abp-studio-browser-catalog-service-swagger-ui](images/abp-studio-browser-catalog-service-swagger-ui.png)
You can test the APIs on the Swagger UI to see if the new microservice is properly working.
### Opening the Catalog Database
The new Catalog microservice has its own database. That database is created automatically by the microservice application, when you run the microservice. Also, [Entity Framework's database migrations](https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/) are automatically applied by the microservice when it runs. So, you don't care about the database schema changes every time you deploy the microservice.
Assuming you've selected SQL Server as your DBMS, you can open the SQL Server Management Studio to see its databases:
![sql-server-management-studio-login-screen](images/sql-server-management-studio-login-screen.png)
Use `localhost,1434` as the *Server name*, select the *SQL Server Authentication* as the *Authentication* type, use `sa` as the *Login* name and `myPassw@rd` as the *Password* value. You can find these values in the `appsettings.json` file in the `CloudCrm.CatalogService` project of the .NET solution of the Catalog microservice.
Once you click the *Connect* button, you can see all the databases and explore their data:
![sql-server-management-studio-databases](images/sql-server-management-studio-databases.png)
The Catalog service's database has only three initial table. The first one is for Entity Framework Core's migration system, and the others are for ABP's [distributed event bus](../../solution-templates/microservice/distributed-events.md) to properly apply transactional events using the outbox and inbox patterns. You don't need to care about these tables since they are created and managed by Entity Framework Core and ABP.
## Summary
In this part of the Microservice Development Tutorial, we added a new Catalog microservice to the solution, explored its code structure and database, and browse its APIs using the Swagger UI. In the next part, we will create functionality in that new microservice.

126
docs/en/tutorials/microservice/part-03.md

@ -0,0 +1,126 @@
# Microservice Tutorial Part 03: Building the Catalog service
````json
//[doc-nav]
{
"Previous": {
"Name": "Creating the initial Catalog service",
"Path": "tutorials/microservice/part-02"
},
"Next": {
"Name": "Creating the initial Ordering service",
"Path": "tutorials/microservice/part-04"
}
}
````
In the previous part, we've created a new microservice named Catalog. In this part, we will build functionality to create and manage products in our system.
In this part, we will use [ABP Suite](../../suite/index.md) to automatically create all the necessary code for us. So, you will see how to use ABP Suite in a microservice solution. We will do everything manually while we will create the Ordering microservice in next parts, so you will learn the details better. We suggest to use ABP Suite wherever it is possible, because it saves a lot of time. You can then investigate the changes done by ABP Suite to understand what it produced.
## Opening the ABP Suite
First of all, **stop all the applications** in ABP Studio's *Solution Runner* panel, because ABP Suite will make changes in the solution and it will also needs to build the solution in some steps. Running the solution prevents to build it.
Now, select the *ABP Suite* -> *Open* command on the main menu to open ABP Suite:
![abp-studio-open-abp-suite](images/abp-studio-open-abp-suite.png)
It will ask to you which module you want to use:
![abp-studio-open-abp-suite-select-module](images/abp-studio-open-abp-suite-select-module.png)
The `CloudCrm` microservice solution contains more than one .NET solution. Typically, each ABP Studio module represents a separate .NET solution (see the [concepts](../../studio/concepts.md) document). ABP Suite works on a single .NET solution to generate code, so we should select a module here.
Select the `CloudCrm.CatalogService` module and click the *OK* button. It will open ABP Suite as shown below:
![abp-studio-abp-suite-inside](images/abp-studio-abp-suite-inside.png)
## Generating a Products Page
In the next section, we will use ABP Suite to create a fully functional CRUD page with ABP Suite. The UI part will be in the main web application (`CloudCrm.Web`) and the application service and other parts will be generated in the Catalog microservice.
### Configuring the Product Entity Information
Type `Product` for the *Name* field and leave the other options as is. ABP Suite will automatically calculate proper values for you:
![abp-suite-product-info](images/abp-suite-product-info.png)
### Configuring Properties of the Product Entity
Open the *Properties* tab and create the properties shown in the following figure:
![abp-suite-product-properties](images/abp-suite-product-properties.png)
Here the details:
* `Name` is required, minimum length is `2` and maximum length is `120`.
* `Description` is not required, it is a *Text area*, not *Filterable*, not *Shown on the list page*.
* `StockCount` has a *Default value* `0`, minimum value `0` and maximum value `999999`.
* `ReleaseDate` is *Nullable*.
You can leave the other configurations as default.
### Generating the Code
![abp-suite-product-generating.png](images/abp-suite-product-generating.png)
That's all. You can click the *Save and generate* button to start the code generation process.
![abp-suite-product-generated](images/abp-suite-product-generated.png)
ABP Suite will generate the necessary code for you. It will take some time to complete the process. After the process is completed, you will see a success message, click the *OK* button.
![abp-studio-catalog-service-build-and-start](images/abp-studio-catalog-service-build-and-start.png)
We can now build and start the `CloudCrm.CatalogService` application by clicking the *Run* -> *Build & Start* button in the *Solution Runner* panel.
![abp-studio-browse-catalog-service-2](images/abp-studio-browse-catalog-service-2.png)
After the application is started, you can right-click and [Browse](../../studio/running-applications.md#monitoring) on the `CloudCrm.CatalogService` application to open it in the ABP Studio's pre-integrated browser. You can see the *Products* controller in the Swagger UI.
### Generating the UI Proxy
Now, we need to generate the [Static API Proxy](../../framework/api-development/static-csharp-clients.md) for the *Web* project. Right-click the *CloudCrm.Web* [package](../../studio/concepts.md#package) and select the *ABP CLI* -> *Generate Proxy* -> *C#* command:
![abp-studio-generate-proxy](images/abp-studio-generate-proxy.png)
It will open the *Generate C# Proxies* window. Select the `CloudCrm.CatalogService` application, and it will automatically populate the *URL* field. Select the *catalog* module, set the service type to *application*, and check the *Without contracts* checkbox, as the `CloudCrm.Web` project already depends on the `CloudCrm.CatalogService.Contracts` package:
![abp-studio-generate-proxy-window](images/abp-studio-generate-proxy-window.png)
> To be able to select the *Application*, you must *Build & Start* the related application beforehand. You can start the application using [Solution Runner](../../studio/running-applications.md) as explained in the previous parts.
Lastly, we need to configure the use of a static HTTP client for the `CatalogService` in the `CloudCrm.Web` project. Open the `CloudCrmWebModule.cs` file in the `Web` project and add the following line to the `ConfigureServices` method:
```csharp
//...
using CloudCrm.CatalogService;
public override void ConfigureServices(ServiceConfigurationContext context)
{
// Code omitted for brevity
context.Services.AddStaticHttpClientProxies(
typeof(CloudCrmCatalogServiceContractsModule).Assembly);
}
```
### Running the Application
Now, stop any application running in the *Solution Runner* panel, and then run the applications by clicking the *Run* -> *Build & Start All* button on the root item in the *Solution Runner* panel:
![abp-studio-run-build-and-start-all](images/abp-studio-run-build-and-start-all.png)
After the application is started, you can right-click and [Browse](../../studio/running-applications.md#monitoring) on the `CloudCrm.Web` application to open it in the ABP Studio's pre-integrated browser:
![abp-studio-browse-cloud-crm-products](images/abp-studio-browse-cloud-crm-products.png)
> If you can't see the *Products* menu item, you need to grant the `CatalogService` *Product* permission to the *admin* role. You can do this by navigating to *Identity Management* -> *Roles* and editing the *admin* role. Alternatively, you can restart the *CloudCrm.AdministrationService* application to automatically seed all permissions for the *admin* role.
You can open the Sql Server Management Studio to see the created tables and data:
![sql-server-management-studio-products](images/sql-server-management-studio-products.png)
## Summary
In this part, we've created a new entity named *Product* and generated the necessary code for it. We've also generated the UI proxy for the `CatalogService` application and configured the static HTTP client for it in the `Web` project. We've run the application and tested the *Products* page.

97
docs/en/tutorials/microservice/part-04.md

@ -0,0 +1,97 @@
# Microservice Tutorial Part 04: Creating the initial Ordering service
````json
//[doc-nav]
{
"Previous": {
"Name": "Building the Catalog service",
"Path": "tutorials/microservice/part-03"
},
"Next": {
"Name": "Building the Ordering service",
"Path": "tutorials/microservice/part-05"
}
}
````
In the previous part, we implemented the Catalog microservice functionality using ABP Suite. In this part, we will create the Ordering microservice, and the following part will cover implementing its functionality manually.
## Creating the Ordering Microservice
Right-click the `services` folder in the *Solution Explorer* panel, select the *Add* -> *New Module* -> *Microservice* command:
![abp-studio-add-new-microservice-command](images/abp-studio-add-new-microservice-command-2.png)
This command opens a new dialog to define the properties of the new microservice. You can use the following values to create a new microservice named `OrderingService`:
![abp-studio-add-new-microservice-dialog](images/abp-studio-add-new-microservice-dialog-2.png)
When you click the *Next* button, you are redirected to the database provider selection step.
### Selecting the Database Type
Here, you can select the database provider to be used by the new microservice:
![abp-studio-add-new-microservice-dialog-database-step](images/abp-studio-add-new-microservice-dialog-database-step.png)
Select *Entity Framework Core* option and proceed the *Next* step.
### Integrating to the Solution
In this step, we can select the options for integrating the new microservice to the rest of the solution components:
![abp-studio-add-new-microservice-dialog-integration-step](images/abp-studio-add-new-microservice-dialog-integration-step.png)
ABP Studio intelligently selects the right values for you, but you should still check them carefully since they directly affect what we will do in the next parts of this tutorial.
**Ensure the options are configured the same as in the preceding figure**, and click the *Next* button.
### Additional Options
![abp-studio-add-new-microservice-dialog-additional-options-step](images/abp-studio-add-new-microservice-dialog-additional-options-step.png)
In this step, you can select additional options for the new microservice. You can leave them as default and click the *Create* button.
That's all, ABP Studio creates the new microservice and arranges all the integration and configuration for you.
## Exploring the New Ordering Microservice
In this section, we will investigate the new microservice in overall.
### Understanding the Solution Structure
Just like the Catalog microservice, the Ordering microservice is a .NET solution that contains multiple projects. You can see the solution structure in the *Solution Explorer* panel:
![abp-studio-solution-explorer-ordering-microservice](images/abp-studio-solution-explorer-ordering-microservice.png)
* `CloudCrm.OrderingService` is the main project that you will implement your service. It typically contains your [entities](../../framework/architecture/domain-driven-design/entities.md), [repositories](../../framework/architecture/domain-driven-design/repositories.md), [application services](../../framework/architecture/domain-driven-design/application-services.md), API controllers, etc.
* `CloudCrm.OrderingService.Contracts` project can be shared with the other services and applications. It typically contains interfaces of your [application services](../../framework/architecture/domain-driven-design/application-services.md), [data transfer objects](../../framework/architecture/domain-driven-design/data-transfer-objects.md), and some other types you may want to share with the clients of this microservice.
* `CloudCrm.OrderingService.Tests` is for building your unit and integration tests for this microservice.
### Running the New Service
You can run the solution using ABP Studio's *Solution Runner*. It will also run the new Ordering service as a part of the solution.
> Before running the solution, **ensure that all the applications are built**. If you are not sure, right-click the root item (`CloudCrm`) in the *Solution Explorer* panel and select the *Build* -> *Graph Build* command.
Click the *Play* button near to the solution root:
![abp-studio-run-solution](images/abp-studio-run-solution.png)
### Browsing the Ordering Service
After the application is started, you can right-click and [Browse](../../studio/running-applications.md#monitoring) on the `CloudCrm.OrderingService` application to open it in the ABP Studio's pre-integrated browser. You can see the *Orders* controller in the Swagger UI:
![abp-studio-browse-ordering-service](images/abp-studio-browse-ordering-service.png)
### Opening the Ordering Database
You can use the SQL Server Management Studio or any other tool to connect to the Ordering service's database. Use `localhost,1434` as the *Server name*, select the *SQL Server Authentication* as the *Authentication* type, use `sa` as the *Login* name and `myPassw@rd` as the *Password* value. You can find these values in the `appsettings.json` file in the `CloudCrm.OrderingService` project of the .NET solution of the Ordering microservice:
![sql-server-management-studio-databases-2](images/sql-server-management-studio-databases-2.png)
Similarly the Ordering service's database has only three initial table. The first one is for Entity Framework Core's migration system, and the others are for ABP's [distributed event bus](../../solution-templates/microservice/distributed-events.md) to properly apply transactional events using the outbox and inbox patterns. You don't need to care about these tables since they are created and managed by Entity Framework Core and ABP.
## Summary
In this part, we've created the initial Ordering microservice. We will implement its functionality in the next part.

430
docs/en/tutorials/microservice/part-05.md

@ -0,0 +1,430 @@
# Microservice Tutorial Part 05: Building the Ordering service
````json
//[doc-nav]
{
"Previous": {
"Name": "Creating the initial Ordering service",
"Path": "tutorials/microservice/part-04"
},
"Next": {
"Name": "Integrating the services: HTTP API Calls",
"Path": "tutorials/microservice/part-06"
}
}
````
In the previous part, we created the Ordering microservice. In this part, we will implement the functionality of the Ordering microservice manually. We will not use ABP Suite to generate the code. Instead, we will create the necessary code step by step to understand the details better.
## Creating the Order Entity
We will start by creating the `Order` entity, which will represent an order in our system. We'll add this entity to the `CloudCrm.OrderingService` project. Create a new folder named `Entities` and add a class named `Order` inside it:
```csharp
using CloudCrm.OrderingService.Enums;
using Volo.Abp.Domain.Entities.Auditing;
namespace CloudCrm.OrderingService.Entities;
public class Order : CreationAuditedAggregateRoot<Guid>
{
public Guid ProductId { get; set; }
public string CustomerName { get; set; }
public OrderState State { get; set; }
}
```
To keep this example simple, we allow users to include only a single product within an order. The `Order` entity inherits from the [CreationAuditedAggregateRoot<>](../../framework/architecture/domain-driven-design/entities.md) class. This class, provided by the ABP Framework, includes common properties like `Id`, `CreationTime`, `CreatorId`, etc.
### Adding the OrderState Enum
We also need to define the `OrderState` enum. In the `CloudCrm.OrderingService.Contracts` project, create a folder named `Enums` and add an `OrderState` enum inside it:
```csharp
namespace CloudCrm.OrderingService.Enums;
public enum OrderState : byte
{
Placed = 0,
Delivered = 1,
Canceled = 2
}
```
The final solution structure should look like this:
![vs-ordering-entity](images/vs-ordering-entity.png)
## Configuring the Database Mapping
First, we need to add the `Order` entity to the `OrderingServiceDbContext` class. Open the `OrderingServiceDbContext` class in the `CloudCrm.OrderingService` project, located in the `Data` folder, and add the following code to the `DbSet` properties:
```csharp
using CloudCrm.OrderingService.Entities;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.DistributedEvents;
using Volo.Abp.EntityFrameworkCore.Modeling;
namespace CloudCrm.OrderingService.Data;
[ConnectionStringName(DatabaseName)]
public class OrderingServiceDbContext :
AbpDbContext<OrderingServiceDbContext>,
IHasEventInbox,
IHasEventOutbox
{
public const string DbTablePrefix = "";
public const string DbSchema = null;
public const string DatabaseName = "OrderingService";
public DbSet<IncomingEventRecord> IncomingEvents { get; set; }
public DbSet<OutgoingEventRecord> OutgoingEvents { get; set; }
public DbSet<Order> Orders { get; set; } // NEW: ADD DBSET PROPERTY
// Code omitted for brevity
}
```
Next, we need to configure the database mapping for the `Order` entity. We'll use the [EF Core Fluent API](https://docs.microsoft.com/en-us/ef/core/modeling/relational/tables) for this configuration. In the `OrderingServiceDbContext` class add the following code to the `OnModelCreating` method:
```csharp
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ConfigureEventInbox();
builder.ConfigureEventOutbox();
builder.Entity<Order>(b =>
{
// Configure table name
b.ToTable("Orders");
// Always call this method to set base entity properties
b.ConfigureByConvention();
// Properties of the entity
b.Property(q => q.CustomerName).IsRequired().HasMaxLength(120);
});
}
```
In this code snippet, we configure the `Order` entity to use the `Orders` table in the database. We also specify that the `CustomerName` property is required and has a maximum length of 120 characters.
### Add a Database Migration
Now, we can add a new database migration. You can use Entity Framework Core's `Add-Migration` (or `dotnet ef migrations add`) terminal command, but in this tutorial, we will use ABP Studio's shortcut UI.
Ensure that the solution has built. You can right-click the `CloudCrm.OrderingService` (under the `services` folder) on ABP Studio *Solution Explorer* and select the *Dotnet CLI* -> *Graph Build* command.
Right-click the `CloudCrm.OrderingService` package and select the *EF Core CLI* -> *Add Migration* command:
![abp-studio-add-entity-framework-core-migration](images/abp-studio-add-entity-framework-core-migration.png)
The *Add Migration* command opens a new dialog to get a migration name:
![abp-studio-entity-framework-core-add-migration-order](images/abp-studio-entity-framework-core-add-migration-order.png)
Once you click the *OK* button, a new database migration class is added to the `Migrations` folder of the `CloudCrm.OrderingService` project:
![visual-studio-new-migration-class](images/visual-studio-new-migration-class.png)
The changes will be applied to the database during the next application startup. For more details, refer to the [database migrations on service startup](../../solution-templates/microservice/database-configurations.md#database-migrations-on-service-startup) section.
## Creating the Application Service
Now, we will create the application service to manage the `Order` entity.
### Defining the Application Service Contract
First, we need to define the application service contract under the `CloudCrm.OrderingService.Contracts` project. Return to your IDE, open the `CloudCrm.OrderingService` module's .NET solution and create an `IOrderAppService` interface under the `Services` folder for the `CloudCrm.OrderingService.Contracts` project:
```csharp
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
namespace CloudCrm.OrderingService.Services;
public interface IOrderAppService : IApplicationService
{
Task<List<OrderDto>> GetListAsync();
Task CreateAsync(OrderCreationDto input);
}
```
### Defining the Data Transfer Objects
Next, we need to define the data transfer objects (DTOs) for the `Order` entity. The `GetListAsync` and `CreateAsync` methods will use these DTOs to communicate with the client applications.
Create a `OrderCreationDto` class under the `Services` folder in the `CloudCrm.OrderingService.Contracts` project:
```csharp
using System;
using System.ComponentModel.DataAnnotations;
namespace CloudCrm.OrderingService.Services;
public class OrderCreationDto
{
[Required]
[StringLength(150)]
public string CustomerName { get; set; }
[Required]
public Guid ProductId { get; set; }
}
```
Create a `OrderDto` class under the `Services` folder in the `CloudCrm.OrderingService.Contracts` project:
```csharp
using System;
using CloudCrm.OrderingService.Enums;
namespace CloudCrm.OrderingService.Services;
public class OrderDto
{
public Guid Id { get; set; }
public string CustomerName { get; set; }
public Guid ProductId { get; set; }
public OrderState State { get; set; }
}
```
The final solution structure should look like this:
![vs-ordering-contracts](images/vs-ordering-contracts.png)
## Implementing the Application Service
Now, we will implement the `IOrderAppService` interface in the `OrderAppService` class. Create an `OrderAppService` class under the `Services` folder in the `CloudCrm.OrderingService` project:
```csharp
using System;
using System.Collections.Generic;
using CloudCrm.OrderingService.Entities;
using CloudCrm.OrderingService.Enums;
using CloudCrm.OrderingService.Localization;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
namespace CloudCrm.OrderingService.Services;
public class OrderAppService : ApplicationService, IOrderAppService
{
private readonly IRepository<Order> _orderRepository;
public OrderAppService(IRepository<Order, Guid> orderRepository)
{
LocalizationResource = typeof(OrderingServiceResource);
_orderRepository = orderRepository;
}
public async Task<List<OrderDto>> GetListAsync()
{
var orders = await _orderRepository.GetListAsync();
return ObjectMapper.Map<List<Order>, List<OrderDto>>(orders);
}
public async Task CreateAsync(OrderCreationDto input)
{
var order = new Order
{
CustomerName = input.CustomerName,
ProductId = input.ProductId,
State = OrderState.Placed
};
await _orderRepository.InsertAsync(order);
}
}
```
In this code snippet, we inject the `IRepository<Order, Guid>` into the `OrderAppService` class. We use this repository to interact with the `Order` entity. The `GetListAsync` method retrieves a list of orders from the database and maps them to the `OrderDto` class. The `CreateAsync` method creates a new order entity and inserts it into the database.
Afterward, we need to configure the *AutoMapper* object to map the `Order` entity to the `OrderDto` class. Open the `OrderingServiceApplicationAutoMapperProfile` class in the `CloudCrm.OrderingService` project, located in the `ObjectMapping` folder, and add the following code:
```csharp
using AutoMapper;
using CloudCrm.OrderingService.Entities;
using CloudCrm.OrderingService.Services;
namespace CloudCrm.OrderingService.ObjectMapping;
public class OrderingServiceApplicationAutoMapperProfile : Profile
{
public OrderingServiceApplicationAutoMapperProfile()
{
CreateMap<Order, OrderDto>();
}
}
```
## Testing the Application Service
Now, we can test the `OrderAppService` class using the Swagger UI. Open the Solution Runner and right-click to `CloudCrm.OrderingService` project and select the *Run* -> *Build & Start* command. After the application starts, you can open the Swagger UI by clicking to the [Browse](../../studio/running-applications.md#monitoring) command:
![ordering-service-swagger-ui](images/ordering-service-swagger-ui.png)
Expand the `api/ordering/order` API and click the *Try it out* button. Then, create a few orders by filling in the request body and clicking the *Execute* button:
![ordering-service-order-swagger-ui](images/ordering-service-order-swagger-ui.png)
If you check the database, you should see the entities created in the `Orders` table:
![sql-server-orders-database-table-records](images/sql-server-orders-database-table-records.png)
> Since we're using the [Auto API Controller](../../framework/api-development/auto-controllers.md) we don't need to create a controller for the `OrderAppService`. The ABP Framework automatically creates an API controller for the `OrderAppService` class. You can find the configuration in the `CloudCrmOrderingServiceModule` class, in the `ConfigureAutoControllers` method of the `CloudCrm.OrderingService` project.
## Creating the User Interface
Now, we will create the user interface for the Ordering module. We will use the `CloudCrm.Web` project to create the user interface. Open the `CloudCrm.Web` .NET solution in your favorite IDE.
### Creating the Orders Page
Create a new `Orders` folder under the `Pages` folder in the `CloudCrm.Web` project. Then, create an `Index.cshtml` Razor Page inside that new folder and edit the `Index.cshtml.cs` file as follows:
```csharp
using CloudCrm.OrderingService.Services;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
namespace CloudCrm.Web.Pages.Orders;
public class Index : AbpPageModel
{
public List<OrderDto> Orders { get; set; }
private readonly IOrderAppService _orderAppService;
public Index(IOrderAppService orderAppService)
{
_orderAppService = orderAppService;
}
public async Task OnGetAsync()
{
Orders = await _orderAppService.GetListAsync();
}
}
```
Here, we inject the `IOrderAppService` into the `Index` Razor Page. We use this service to retrieve the list of orders from the database and assign them to the `Orders` property. Open the `Index.cshtml` file and add the following code:
```html
@page
@model CloudCrm.Web.Pages.Orders.Index
<h1>Orders</h1>
<abp-card>
<abp-card-body>
<abp-list-group>
@foreach (var order in Model.Orders)
{
<abp-list-group-item>
<strong>Customer:</strong> @order.CustomerName <br />
<strong>Product:</strong> @order.ProductId <br />
<strong>State:</strong> @order.State
</abp-list-group-item>
}
</abp-list-group>
</abp-card-body>
</abp-card>
```
This page shows a list of orders on the UI. We haven't created a UI to create new orders, and we will not do it to keep this tutorial simple. If you want to learn how to create advanced UIs with ABP, please follow the [Book Store tutorial](../../tutorials/book-store/index.md).
### Generating the UI Proxy
To select the *Application* during proxy generation, ensure that the `CloudCrm.OrderingService` is *Started* beforehand. You can start the application using [Solution Runner](../../studio/running-applications.md).
Now, we need to generate the [Static API Proxy](../../framework/api-development/static-csharp-clients.md) for the *Web* project. Right-click the *CloudCrm.Web* [package](../../studio/concepts.md#package) and select the *ABP CLI* -> *Generate Proxy* -> *C#* command:
![abp-studio-generate-proxy-2](images/abp-studio-generate-proxy-2.png)
It will open the *Generate C# Proxies* window. Select the `CloudCrm.OrderingService` application, and it will automatically populate the *URL* field. Choose the *ordering* module and service type is *application* lastly check the *Without contracts* checkbox, since we already have a dependency on the `CloudCrm.OrderingService.Contracts` package in the `CloudCrm.Web` project:
![abp-studio-generate-proxy-window-ordering-module](images/abp-studio-generate-proxy-window-ordering-module.png)
Lastly, we need to configure the use of a static HTTP client for the `OrderingService` in the `CloudCrm.Web` project. Open the `CloudCrmWebModule.cs` file in the `Web` project and add the following line to the `ConfigureServices` method:
```csharp
//...
using CloudCrm.OrderingService;
public override void ConfigureServices(ServiceConfigurationContext context)
{
// Code omitted for brevity
context.Services.AddStaticHttpClientProxies(
typeof(CloudCrmOrderingServiceContractsModule).Assembly);
}
```
### Adding the Menu Item
> ABP provides a modular navigation [menu system](../../framework/ui/mvc-razor-pages/navigation-menu.md) that allows you to define the menu items in a modular way.
Finally, we need to add a menu item to the sidebar to navigate to the `Orders` page. Open the `CloudCrmMenus` file in the `Navigation` folder of the `CloudCrm.Web` project and edit with the following code:
```csharp
namespace CloudCrm.Web.Navigation;
public class CloudCrmMenus
{
private const string Prefix = "CloudCrm";
public const string Home = Prefix + ".Home";
public const string HostDashboard = Prefix + ".HostDashboard";
public const string TenantDashboard = Prefix + ".TenantDashboard";
public const string Products = Prefix + ".Products";
public const string Orders = Prefix + ".Orders"; // NEW: ADD MENU ITEM
}
```
Then, open the `CloudCrmMenuContributor` class in the `CloudCrm.Web` project, located in the `Navigation` folder, and add the following code to `ConfigureMainMenuAsync` method:
```csharp
private static async Task ConfigureMainMenuAsync(MenuConfigurationContext context)
{
// Code omitted for brevity
context.Menu.AddItem(
new ApplicationMenuItem(
CloudCrmMenus.Orders, // Unique menu id
"Orders", // Menu display text
"~/Orders", // URL
"fa-solid fa-basket-shopping" // Icon CSS class
)
);
}
```
## Building and Running the Application
Now, we can build and run the application to see the changes. Please stop the applications if they are running. Then open the *Solution Runner* panel, right-click the `CloudCrm` root item, and select the *Run* -> *Build & Start* command:
![abp-studio-run-build-start](images/abp-studio-run-build-start.png)
After the applications are started, you can *Browse* and navigate to the `Orders` page to see the list of orders:
![web-orders-page](images/web-orders-page.png)
Great! We have successfully implemented the Ordering module. However, there is a problem:
- We see Product's GUID ID instead of its name. This is because the *Ordering* microservice has no integration with the *Catalog* microservice and doesn't have access to Product microservice's database to perform a JOIN query.
We will solve this problem in the next part by implementing an integration service between the *Ordering* and *Catalog* microservices.
## Summary
In this part, we implemented the Ordering module manually. We created the `Order` entity, the `OrderState` enum, the `OrderAppService` application service, and the user interface for the `Orders` page. We also added a menu item to the sidebar to navigate to the `Orders` page.

311
docs/en/tutorials/microservice/part-06.md

@ -0,0 +1,311 @@
# Microservice Tutorial Part 06: Integrating the services: HTTP API Calls
````json
//[doc-nav]
{
"Previous": {
"Name": "Building the Ordering service",
"Path": "tutorials/microservice/part-05"
},
"Next": {
"Name": "Integrating the services: Using Distributed Events",
"Path": "tutorials/microservice/part-07"
}
}
````
In the previous part, we implemented the functionality of the Ordering microservice. However, when listing orders, we need to display the product name instead of the product ID. To achieve this, we must call the Catalog service to retrieve the product name for each order item.
In this section, we will integrate the Ordering service with the Catalog service using HTTP API calls.
## The Need for the Integration Services
In a microservices architecture, each service is responsible for its own data and business logic. However, services often need to communicate with each other to fulfill their responsibilities. This communication can be synchronous or asynchronous, depending on the requirements.
![web-orders-page](images/web-orders-page.png)
In our case, the Ordering service needs to display the product name instead of the product ID. To achieve this, we need to call the Catalog service to retrieve the product details based on the product ID. This is a typical example of a synchronous communication pattern between microservices. As a solution to that problem, we will use an [integration service](../../framework/api-development/integration-services.md) that will handle the communication with the Catalog service. Integration service concept in ABP is designed for request/response style inter-module (in modular applications) and inter-microservice (in distributed systems) communication.
## Creating a Products Integration Service
First, we need to create a service that will handle the communication with the Catalog service. This service will be responsible for fetching the product details based on the product ID.
### Defining the `IProductIntegrationService` Interface
Open the `CloudCrm.CatalogService` .NET solution in your IDE. Locate the `CloudCrm.CatalogService.Contracts` project, and create a new folder named `IntegrationServices`. Inside this folder, add a new interface named `IProductIntegrationService` with the following code:
```csharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using CloudCrm.CatalogService.Products;
using Volo.Abp;
using Volo.Abp.Application.Services;
namespace CloudCrm.CatalogService.IntegrationServices;
[IntegrationService]
public interface IProductIntegrationService : IApplicationService
{
Task<List<ProductDto>> GetProductsByIdsAsync(List<Guid> ids);
}
```
`IProductIntegrationService` is very similar to a typical [application service](../../framework/architecture/domain-driven-design/application-services.md). The only difference is that it is marked with the `[IntegrationService]` attribute. This attribute is used to identify the service as an integration service, which allows ABP to handle the communication between services. ABP behave differently for them (for example, ABP doesn't expose [integration services](../../framework/api-development/integration-services.md) as HTTP APIs by default if you've configured the [Auto API Controllers](../../framework/api-development/auto-controllers.md) feature)
`IProductIntegrationService` contains a single method named `GetProductsByIdsAsync`. This method takes a list of product IDs and returns a list of `ProductDto` objects. This is exactly what we need in the Ordering service.
> **Design Tip**
>
> You may think if we can use the existing application services (like `IProductAppService`) from other services instead of creating specific integration services. Technically you can use, ABP has no restriction. However, from good design and best practice points, we don't suggest it. Because, application services are designed to be consumed specifically by the presentation layer. They will have different authorization and validation logic, they will need different DTO input and output properties, they will have different performance, optimization and caching requirements, and so on. And most importantly, all these will change by the time based on UI requirements and these changes may break your integrations later. It is best to implement specific integration APIs that is designed and optimized for that purpose.
>
> We've reused the `ProductDto` object created for `IProductAppService`, which can be reasonable from a maintenance point of view. But if you think your integration service results will be different from the application service results in the future, it can be good to separate them from the first day so you don't need to introduce breaking changes later.
### Implementing the `ProductIntegrationService` Class
Now, let's implement the `IProductIntegrationService` interface. Create a new folder named `IntegrationServices` in the `CloudCrm.CatalogService` project. Inside this folder, add a new class named `ProductIntegrationService` with the following code:
```csharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using CloudCrm.CatalogService.Localization;
using CloudCrm.CatalogService.Products;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
namespace CloudCrm.CatalogService.IntegrationServices;
[IntegrationService]
public class ProductIntegrationService : ApplicationService, IProductIntegrationService
{
private readonly IRepository<Product, Guid> _productRepository;
public ProductIntegrationService(IRepository<Product, Guid> productRepository)
{
LocalizationResource = typeof(CatalogServiceResource);
_productRepository = productRepository;
}
public async Task<List<ProductDto>> GetProductsByIdsAsync(List<Guid> ids)
{
var products = await _productRepository.GetListAsync(
product => ids.Contains(product.Id)
);
return ObjectMapper.Map<List<Product>, List<ProductDto>>(products);
}
}
```
`ProductIntegrationService` is a typical application service class that implements the `IProductIntegrationService` interface. It has a constructor that takes an `IRepository<Product, Guid>` object. This repository is used to fetch the product details from the database.
> Here, we directly used `List<T>` classes, but instead, you could wrap inputs and outputs into [DTOs](../../framework/architecture/domain-driven-design/data-transfer-objects.md). In that way, it can be possible to add new properties to these DTOs without changing the signature of your integration service method (and without introducing breaking changes for your client applications).
### Exposing the Integration Service as an API
Integration services are not exposed as HTTP APIs by default. However, you can expose them as HTTP APIs if you need to. To do this, you should configure the `AbpAspNetCoreMvcOptions` in the `ConfigureServices` method of the `CloudCrmCatalogServiceModule`. Open the `CloudCrm.CatalogService` project and locate the `CloudCrmCatalogServiceModule` class. Add the following code to the `ConfigureServices` method:
```csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
// Other configurations...
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ExposeIntegrationServices = true;
});
}
```
This code configures the `AbpAspNetCoreMvcOptions` to expose integration services as HTTP APIs. This is useful when you need to call the integration service from a different service using HTTP. You can learn more about this in the [Integration Services](../../framework/api-development/integration-services.md#exposing-integration-services) document.
## Consuming the Products Integration Service
Now that we have created the `IProductIntegrationService` interface and the `ProductIntegrationService` class, we can consume this service from the Ordering service.
### Adding a Reference to the `CloudCrm.CatalogService.Contracts` Package
First, we need to add a reference to the `CloudCrm.CatalogService.Contracts` package in the Ordering service. Open the ABP Studio, and stop the application(s) if it is running. Then, open the *Solution Explorer* and right-click on the `CloudCrm.OrderingService` package. Select *Add* -> *Package Reference* command:
![add-package-reference-ordering-service](images/add-package-reference-ordering-service.png)
In the *Add Package Reference* window, select the `CloudCrm.CatalogService.Contracts` package from the *This solution* tab. Click the *OK* button to add the reference:
![add-catalog-service-contracts-reference](images/add-catalog-service-contracts-reference.png)
ABP Studio adds the package reference and arranges the [module](../../framework/architecture/modularity/basics.md) dependency.
> Instead of directly adding such a package reference, it can be best to import the module first (right-click the `CloudCrm.OrderingService`, select the _Import Module_ command and import the `CloudCrm.CatalogService` module), then install the package reference. In that way, it would be easy to see and keep track of inter-module dependencies.
### Using the Products Integration Service
Now, we can use the `IProductIntegrationService` interface to fetch the product details in the `OrderAppService` class.
Open the `OrderAppService` class (the `OrderAppService.cs` file under the `Services` folder of the `CloudCrm.OrderingService` project of the `CloudCrm.OrderingService` .NET solution) and change its content as like the following code block:
```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CloudCrm.CatalogService.IntegrationServices;
using CloudCrm.OrderingService.Entities;
using CloudCrm.OrderingService.Enums;
using CloudCrm.OrderingService.Localization;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
namespace CloudCrm.OrderingService.Services;
public class OrderAppService : ApplicationService, IOrderAppService
{
private readonly IRepository<Order> _orderRepository;
private readonly IProductIntegrationService _productIntegrationService;
public OrderAppService(
IRepository<Order, Guid> orderRepository,
IProductIntegrationService productIntegrationService)
{
LocalizationResource = typeof(OrderingServiceResource);
_orderRepository = orderRepository;
_productIntegrationService = productIntegrationService;
}
public async Task<List<OrderDto>> GetListAsync()
{
var orders = await _orderRepository.GetListAsync();
// Prepare a list of products we need
var productIds = orders.Select(o => o.ProductId).Distinct().ToList();
var products = (await _productIntegrationService
.GetProductsByIdsAsync(productIds))
.ToDictionary(p => p.Id, p => p.Name);
var orderDtos = ObjectMapper.Map<List<Order>, List<OrderDto>>(orders);
orderDtos.ForEach(orderDto =>
{
orderDto.ProductName = products[orderDto.ProductId];
});
return orderDtos;
}
public async Task CreateAsync(OrderCreationDto input)
{
var order = new Order
{
CustomerName = input.CustomerName,
ProductId = input.ProductId,
State = OrderState.Placed
};
await _orderRepository.InsertAsync(order);
}
}
```
Now open the `OrderDto` class (the `OrderDto.cs` file under the `Services` folder of the `CloudCrm.OrderingService.Contracts` project of the `CloudCrm.OrderingService` .NET solution) and add a new property named `ProductName`:
```csharp
using System;
using CloudCrm.OrderingService.Enums;
namespace CloudCrm.OrderingService.Services;
public class OrderDto
{
public Guid Id { get; set; }
public string CustomerName { get; set; }
public Guid ProductId { get; set; }
public string ProductName { get; set; } // New property
public OrderState State { get; set; }
}
```
Lastly, open the `OrderingServiceApplicationAutoMapperProfile` class (the `OrderingServiceApplicationAutoMapperProfile.cs` file under the `ObjectMapping` folder of the `CloudCrm.OrderingService` project of the `CloudCrm.OrderingService` .NET solution) and ignore the `ProductName` property in the mapping configuration:
```csharp
using AutoMapper;
using CloudCrm.OrderingService.Entities;
using CloudCrm.OrderingService.Services;
using Volo.Abp.AutoMapper;
namespace CloudCrm.OrderingService.ObjectMapping;
public class OrderingServiceApplicationAutoMapperProfile : Profile
{
public OrderingServiceApplicationAutoMapperProfile()
{
CreateMap<Order, OrderDto>()
.Ignore(x => x.ProductName); // New line
}
}
```
Let's explain the changes we made:
- We added a new property named `ProductName` to the `OrderDto` class. This property will hold the product name.
- We modified the `GetListAsync` method of the `OrderAppService` class to fetch the product details using the `IProductIntegrationService` interface. We first fetch the product IDs from the orders, then call the `GetProductsByIdsAsync` method of the `IProductIntegrationService` interface to fetch the product details. Finally, we map the product names to the `OrderDto` objects.
### Generating Proxy Classes for the Integration Service
We have created the `IProductIntegrationService` interface and the `ProductIntegrationService` class in the `CloudCrm.CatalogService` solution. Now, we need to generate the proxy classes for the integration service in the `CloudCrm.OrderingService` package. First, *Build & Start* the `CloudCrm.CatalogService` application in ABP Studio *Solution Runner*. Then, open the *Solution Explorer* and right-click on the `CloudCrm.OrderingService` package. Select the *ABP CLI* -> *Generate Proxy* -> *C#* command:
![generate-proxy-catalog-service](images/generate-proxy-catalog-service.png)
It opens the *Generate C# proxies* window. Select the `CloudCrm.CatalogService` application from the *Application* dropdown list. Then, choose the *catalog* module from the *Module* dropdown list and choose the *integration* service from the *Service type* dropdown list. Check the *Without contracts* checkbox and click the *Generate* button:
![generate-catalog-service-proxy](images/generate-catalog-service-proxy.png)
We have generated the proxy classes for the `IProductIntegrationService` interface. Now, we must add the *Remote Service* url to the `appsettings.json` file of the `CloudCrm.OrderingService` project. Open the `appsettings.json` file (the `appsettings.json` file of the `CloudCrm.OrderingService` project of the `CloudCrm.OrderingService` .NET solution) and add the *CatalogService* section following configuration:
```json
{
"RemoteServices": {
"CatalogService": {
"BaseUrl": "http://localhost:44334"
}
}
}
```
> **BaseUrl** refers to the base URL of the Catalog service. You can use the *Copy Url* option from the Catalog service's context menu in the ABP Studio **Solution Runner** to paste it here.
### Updating the UI to Display the Product Name
Open the `Index.cshtml` file (the `Index.cshtml` file under the `Pages/Orders` folder of the `CloudCrm.Web` project of the `CloudCrm.Web` .NET solution) and update the table content to display the product name instead of the product ID:
```html
@page
@model CloudCrm.Web.Pages.Orders.Index
<h1>Orders</h1>
<abp-card>
<abp-card-body>
<abp-list-group>
@foreach (var order in Model.Orders)
{
<abp-list-group-item>
<strong>Customer:</strong> @order.CustomerName <br />
<strong>Product:</strong> @order.ProductName <br />
<strong>State:</strong> @order.State
</abp-list-group-item>
}
</abp-list-group>
</abp-card-body>
</abp-card>
```
That's it! Now, you can *Build & Start* the all applications and run it in ABP Studio to see the result:
![web-orders-page-with-product-name](images/web-orders-page-with-product-name.png)
Now, the Ordering service displays the product name instead of the product ID. We have successfully integrated the Ordering service with the Catalog service using HTTP API calls.
> **Design Tip**
>
> It is suggested that you keep that type of communication to a minimum and not couple your services with each other. It can make your solution complicated and may also decrease your system performance. When you need to do it, think about performance and try to make some optimizations. For example, if the Ordering service frequently needs product data, you can use a kind of [cache layer](../../framework/fundamentals/caching.md), so it doesn't make frequent requests to the Catalog service.

231
docs/en/tutorials/microservice/part-07.md

@ -0,0 +1,231 @@
# Microservice Tutorial Part 07: Integrating the services: Using Distributed Events
````json
//[doc-nav]
{
"Previous": {
"Name": "Integrating the services: HTTP API Calls",
"Path": "tutorials/microservice/part-06"
}
}
````
Another common approach to communicating between microservices is messaging. By publishing and handling messages, a microservice can perform an operation when an event happens in another microservice.
ABP provides two types of event buses for loosely coupled communication:
* [Local Event Bus](../../framework/infrastructure/event-bus/local/index.md) is suitable for in-process messaging. However, it’s not suitable for microservices as it cannot communicate across different processes. For distributed systems, consider using a distributed event bus.
* **[Distributed Event Bus](../../framework/infrastructure/event-bus/distributed/index.md)** is normal for inter-process messaging, like microservices, for publishing and subscribing to distributed events. However, ABP's distributed event bus works as local (in-process) by default (actually, it uses the Local Event Bus under the hood by default) unless you configure an external message broker.
In this tutorial, we will use the distributed event bus to communicate between the `Order` and `Catalog` microservices.
## Publishing an Event
In the example scenario, we want to publish an event when a new order is placed. The Ordering service will publish the event since it knows when a new order is placed. The Catalog service will subscribe to that event and get notified when a new order is placed. This will decrease the stock count of the product related to the new order. The scenario is pretty simple; let's implement it.
### Defining the Event Class
Open the `CloudCrm.OrderingService` .NET solution in your IDE, create an `Events` folder and create a new class named `OrderPlacedEto` under the `CloudCrm.OrderingService.Contracts` project:
```csharp
using System;
namespace CloudCrm.OrderingService.Events;
public class OrderPlacedEto
{
public string CustomerName { get; set; }
public Guid ProductId { get; set; }
}
```
`OrderPlacedEto` is very simple. It is a plain C# class used to transfer data related to the event (*ETO* is an acronym for *Event Transfer Object*, a suggested naming convention but not required). You can add more properties if needed, but for this tutorial, it is more than enough.
### Using the `IDistributedEventBus` Service
The `IDistributedEventBus` service publishes events to the event bus. Until this point, the Ordering service only creates an Order and insert to database. Let's change that and publish `OrderPlacedEto` event, for that purpose open the `CloudCrm.OrderingService` project and update to the `OrderAppService` class as shown below:
```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CloudCrm.CatalogService.IntegrationServices;
using CloudCrm.OrderingService.Entities;
using CloudCrm.OrderingService.Enums;
using CloudCrm.OrderingService.Events;
using CloudCrm.OrderingService.Localization;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.EventBus.Distributed;
namespace CloudCrm.OrderingService.Services;
public class OrderAppService : ApplicationService, IOrderAppService
{
private readonly IRepository<Order> _orderRepository;
private readonly IProductIntegrationService _productIntegrationService;
private readonly IDistributedEventBus _distributedEventBus;
public OrderAppService(
IRepository<Order, Guid> orderRepository,
IProductIntegrationService productIntegrationService,
IDistributedEventBus distributedEventBus)
{
LocalizationResource = typeof(OrderingServiceResource);
_orderRepository = orderRepository;
_productIntegrationService = productIntegrationService;
_distributedEventBus = distributedEventBus;
}
public async Task<List<OrderDto>> GetListAsync()
{
var orders = await _orderRepository.GetListAsync();
// Prepare a list of products we need
var productIds = orders.Select(o => o.ProductId).Distinct().ToList();
var products = (await _productIntegrationService
.GetProductsByIdsAsync(productIds))
.ToDictionary(p => p.Id, p => p.Name);
var orderDtos = ObjectMapper.Map<List<Order>, List<OrderDto>>(orders);
orderDtos.ForEach(orderDto =>
{
orderDto.ProductName = products[orderDto.ProductId];
});
return orderDtos;
}
public async Task CreateAsync(OrderCreationDto input)
{
// Create a new Order entity
var order = new Order
{
CustomerName = input.CustomerName,
ProductId = input.ProductId,
State = OrderState.Placed
};
// Save it to the database
await _orderRepository.InsertAsync(order);
// Publish an event so other microservices can be informed
await _distributedEventBus.PublishAsync(
new OrderPlacedEto
{
ProductId = order.ProductId,
CustomerName = order.CustomerName
});
}
}
```
The `OrderAppService.CreateAsync` method creates a new `Order` entity, saves it to the database and finally publishes an `OrderPlacedEto` event.
## Subscribing to an Event
The Catalog service will subscribe to the `OrderPlacedEto` event and decrease the stock count of the product related to the new order. Let's implement it.
### Adding a Reference to the `CloudCrm.OrderingService.Contracts` Package
Since the `OrderPlacedEto` class is in the `CloudCrm.OrderingService.Contracts` project, we must add that package's reference to the Catalog service. This time, we will use the Import Module feature of ABP Studio (as an alternative to the approach we used in the Adding a Reference to the `CloudCrm.CatalogService.Contracts` Package section of the [previous part](./part-06.md#adding-a-reference-to-the-cloudcrmcatalogservicecontracts-package)).
Open the ABP Studio UI and stop the applications if they are running. Then, open the *Solution Explorer* panel and right-click on the `CloudCrm.CatalogService`. Select *Import Module* from the context menu:
![Import Module](images/import-module.png)
In the opening dialog, find and select the `CloudCrm.OrderingService` module, check the *Install this module* option, click the *OK* button:
![Import Module Dialog](images/import-module-dialog.png)
Once you click the OK button, the Ordering service is imported to the Catalog service. It opens the *Install Module* dialog:
![Install Module Dialog](images/install-module-dialog.png)
Here, select the `CloudCrm.OrderingService.Contracts` package on the left side (because we want to add that package reference) and `CloudCrm.CatalogService` package on the middle area (because we want to add the package reference to that project).
You can check the ABP Studio's *Solution Explorer* panel to see the module and the project reference (dependency):
![catalog-service-dependency](images/catalog-service-dependency.png)
### Handling the `OrderPlacedEto` Event
Now, it's time to handle the `OrderPlacedEto` event in the Catalog service. Open the `CloudCrm.CatalogService` .NET solution in your IDE. Create a new `Orders` folder, and add a new class named `OrderEventHandler` inside that folder within the `CloudCrm.CatalogService` project:
```csharp
using System;
using System.Threading.Tasks;
using CloudCrm.CatalogService.Products;
using CloudCrm.OrderingService.Events;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.EventBus.Distributed;
namespace CloudCrm.CatalogService.Orders;
public class OrderEventHandler :
IDistributedEventHandler<OrderPlacedEto>,
ITransientDependency
{
private readonly IRepository<Product, Guid> _productRepository;
public OrderEventHandler(IRepository<Product, Guid> productRepository)
{
_productRepository = productRepository;
}
public async Task HandleEventAsync(OrderPlacedEto eventData)
{
// Find the related product
var product = await _productRepository.FindAsync(eventData.ProductId);
if (product == null)
{
return;
}
// Decrease the stock count
product.StockCount = product.StockCount - 1;
// Update the entity in the database
await _productRepository.UpdateAsync(product);
}
}
```
The `OrderEventHandler` class implements the `IDistributedEventHandler<OrderPlacedEto>` interface to handle the `OrderPlacedEto` event. When the event is published, the `HandleEventAsync` method is called. In this method, we find the related product, decrease the stock count by one, and update the entity in the database.
Implementing `ITransientDependency` registers the `OrderEventHandler` class to the dependency injection system as a transient object.
### Testing the Order Creation
To keep this tutorial simple, we will not implement a user interface for creating orders. Instead, we will use the Swagger UI to create an order. Open the *Solution Runner* panel in ABP Studio and use *Build & Start* to launch the `CloudCrm.OrderingService` and `CloudCrm.CatalogService` applications. Then, go to *Run* -> *Start All* to start the remaining applications listed in the [Solution Runner root item](../../studio/running-applications.md#run).
Once the application is running and ready, [Browse](../../studio/running-applications.md#c-application) the `CloudCrm.OrderingService` application. Use the `POST /api/ordering/order` endpoint to create a new order:
![Create Order](images/create-order.png)
Find the *Order* API, click the *Try it out* button, enter a sample value the *Request body* section, and click the *Execute* button:
```json
{
"customerName": "David",
"productId": "5995897b-1de9-7272-b31c-3a165bbe7b18"
}
```
> **IMPORTANT:** Here, you should type a valid Product Id from the Products table of your database!
Once you press the *Execute* button, a new order is created. At that point, you can check the `/Orders` page to see if the new order is listed. You can also check the `/Products` page to see if the stock count of the related product is decreased by one in the `CloudCrm.Web` application.
Here are sample screenshots from the Orders and Products pages of the `CloudCrm.Web` application:
![Orders](images/orders.png)
We placed a new order for *Product A*. As a result, the stock count of *Product A* is decreased from 53 to 52 and a new line is added to the Orders page.
## Conclusion
In this tutorial, we used the distributed event bus to communicate between the `Order` and `Catalog` microservices. We published an event when a new order is placed and handled that event in the Catalog service to decrease the stock count of the related product. This is a simple example, but it shows how you can use distributed events to communicate between microservices.

2
docs/en/tutorials/modular-crm/part-02.md

@ -14,7 +14,7 @@
}
````
In this part, you will build a new product management module and install it in the main CRM application.
In this part, you will create a new product management module and install it in the main CRM application.
## Creating Solution Folders

Loading…
Cancel
Save