diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index d30b25dfb6..53f1159629 100644 --- a/docs/en/docs-nav.json +++ b/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" diff --git a/docs/en/get-started/images/abp-studio-microservice-solution-runner-docker-dependencies.png b/docs/en/get-started/images/abp-studio-microservice-solution-runner-docker-dependencies.png deleted file mode 100644 index 99c09045f8..0000000000 Binary files a/docs/en/get-started/images/abp-studio-microservice-solution-runner-docker-dependencies.png and /dev/null differ diff --git a/docs/en/get-started/microservice.md b/docs/en/get-started/microservice.md index 5be705e215..7a94ae3bdf 100644 --- a/docs/en/get-started/microservice.md +++ b/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) diff --git a/docs/en/tutorials/index.md b/docs/en/tutorials/index.md index 1f03c87d8e..67edb5eaeb 100644 --- a/docs/en/tutorials/index.md +++ b/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. diff --git a/docs/en/tutorials/microservice/images/abp-studio-abp-suite-inside.png b/docs/en/tutorials/microservice/images/abp-studio-abp-suite-inside.png new file mode 100644 index 0000000000..19ea31ae3e Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-abp-suite-inside.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-add-entity-framework-core-migration.png b/docs/en/tutorials/microservice/images/abp-studio-add-entity-framework-core-migration.png new file mode 100644 index 0000000000..ea84d9aaec Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-add-entity-framework-core-migration.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-command-2.png b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-command-2.png new file mode 100644 index 0000000000..62908bd061 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-command-2.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-command.png b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-command.png new file mode 100644 index 0000000000..ac61c70a09 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-command.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-2.png b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-2.png new file mode 100644 index 0000000000..82a5dec467 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-2.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-additional-options-step.png b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-additional-options-step.png new file mode 100644 index 0000000000..c32dd7965c Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-additional-options-step.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-database-step.png b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-database-step.png new file mode 100644 index 0000000000..6d249f0a51 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-database-step.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-integration-step.png b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-integration-step.png new file mode 100644 index 0000000000..133e4b8039 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-integration-step.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog.png b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog.png new file mode 100644 index 0000000000..ae88df296b Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-browse-catalog-service-2.png b/docs/en/tutorials/microservice/images/abp-studio-browse-catalog-service-2.png new file mode 100644 index 0000000000..680104c724 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-browse-catalog-service-2.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-browse-catalog-service.png b/docs/en/tutorials/microservice/images/abp-studio-browse-catalog-service.png new file mode 100644 index 0000000000..2b3e72a60b Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-browse-catalog-service.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-browse-cloud-crm-products.png b/docs/en/tutorials/microservice/images/abp-studio-browse-cloud-crm-products.png new file mode 100644 index 0000000000..fa6c2cc503 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-browse-cloud-crm-products.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-browse-ordering-service.png b/docs/en/tutorials/microservice/images/abp-studio-browse-ordering-service.png new file mode 100644 index 0000000000..c009dd3554 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-browse-ordering-service.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-browser-catalog-service-swagger-ui.png b/docs/en/tutorials/microservice/images/abp-studio-browser-catalog-service-swagger-ui.png new file mode 100644 index 0000000000..2b49ecd538 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-browser-catalog-service-swagger-ui.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-catalog-service-build-and-start.png b/docs/en/tutorials/microservice/images/abp-studio-catalog-service-build-and-start.png new file mode 100644 index 0000000000..a69bcf1383 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-catalog-service-build-and-start.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-entity-framework-core-add-migration-order.png b/docs/en/tutorials/microservice/images/abp-studio-entity-framework-core-add-migration-order.png new file mode 100644 index 0000000000..aa9861a3c6 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-entity-framework-core-add-migration-order.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-2.png b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-2.png new file mode 100644 index 0000000000..6b93e2b8ec Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-2.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-window-ordering-module.png b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-window-ordering-module.png new file mode 100644 index 0000000000..df536782d5 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-window-ordering-module.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-window.png b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-window.png new file mode 100644 index 0000000000..5cb0ff188b Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-window.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-generate-proxy.png b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy.png new file mode 100644 index 0000000000..96ecd2c400 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-new-catalog-service-in-solution-explorer.png b/docs/en/tutorials/microservice/images/abp-studio-new-catalog-service-in-solution-explorer.png new file mode 100644 index 0000000000..b358ac07d7 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-new-catalog-service-in-solution-explorer.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-open-abp-suite-select-module.png b/docs/en/tutorials/microservice/images/abp-studio-open-abp-suite-select-module.png new file mode 100644 index 0000000000..564c93e975 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-open-abp-suite-select-module.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-open-abp-suite.png b/docs/en/tutorials/microservice/images/abp-studio-open-abp-suite.png new file mode 100644 index 0000000000..9073d61b01 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-open-abp-suite.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-open-with-visual-studio.png b/docs/en/tutorials/microservice/images/abp-studio-open-with-visual-studio.png new file mode 100644 index 0000000000..361aa7a1d1 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-open-with-visual-studio.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-run-build-and-start-all.png b/docs/en/tutorials/microservice/images/abp-studio-run-build-and-start-all.png new file mode 100644 index 0000000000..f1e254bfd1 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-run-build-and-start-all.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-run-build-start.png b/docs/en/tutorials/microservice/images/abp-studio-run-build-start.png new file mode 100644 index 0000000000..942e75d1b6 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-run-build-start.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-run-solution.png b/docs/en/tutorials/microservice/images/abp-studio-run-solution.png new file mode 100644 index 0000000000..a1602112a4 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-run-solution.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-solution-explorer-initial-cloud-crm-microservice-solution.png b/docs/en/tutorials/microservice/images/abp-studio-solution-explorer-initial-cloud-crm-microservice-solution.png new file mode 100644 index 0000000000..b442be04d8 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-solution-explorer-initial-cloud-crm-microservice-solution.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-solution-explorer-ordering-microservice.png b/docs/en/tutorials/microservice/images/abp-studio-solution-explorer-ordering-microservice.png new file mode 100644 index 0000000000..ab32985ae5 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-solution-explorer-ordering-microservice.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-solution-runner-play-all.png b/docs/en/tutorials/microservice/images/abp-studio-solution-runner-play-all.png new file mode 100644 index 0000000000..9df793bdad Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-solution-runner-play-all.png differ diff --git a/docs/en/tutorials/microservice/images/abp-suite-product-generated.png b/docs/en/tutorials/microservice/images/abp-suite-product-generated.png new file mode 100644 index 0000000000..7e8ca7bf7f Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-suite-product-generated.png differ diff --git a/docs/en/tutorials/microservice/images/abp-suite-product-generating.png b/docs/en/tutorials/microservice/images/abp-suite-product-generating.png new file mode 100644 index 0000000000..92a248aa91 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-suite-product-generating.png differ diff --git a/docs/en/tutorials/microservice/images/abp-suite-product-info.png b/docs/en/tutorials/microservice/images/abp-suite-product-info.png new file mode 100644 index 0000000000..a68f19030e Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-suite-product-info.png differ diff --git a/docs/en/tutorials/microservice/images/abp-suite-product-properties.png b/docs/en/tutorials/microservice/images/abp-suite-product-properties.png new file mode 100644 index 0000000000..cecc40b8cf Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-suite-product-properties.png differ diff --git a/docs/en/tutorials/microservice/images/add-catalog-service-contracts-reference.png b/docs/en/tutorials/microservice/images/add-catalog-service-contracts-reference.png new file mode 100644 index 0000000000..e7caaa0138 Binary files /dev/null and b/docs/en/tutorials/microservice/images/add-catalog-service-contracts-reference.png differ diff --git a/docs/en/tutorials/microservice/images/add-package-reference-ordering-service.png b/docs/en/tutorials/microservice/images/add-package-reference-ordering-service.png new file mode 100644 index 0000000000..a5894b85f6 Binary files /dev/null and b/docs/en/tutorials/microservice/images/add-package-reference-ordering-service.png differ diff --git a/docs/en/tutorials/microservice/images/catalog-service-dependency.png b/docs/en/tutorials/microservice/images/catalog-service-dependency.png new file mode 100644 index 0000000000..695e15b79b Binary files /dev/null and b/docs/en/tutorials/microservice/images/catalog-service-dependency.png differ diff --git a/docs/en/tutorials/microservice/images/create-order.png b/docs/en/tutorials/microservice/images/create-order.png new file mode 100644 index 0000000000..e4b9cb5219 Binary files /dev/null and b/docs/en/tutorials/microservice/images/create-order.png differ diff --git a/docs/en/tutorials/microservice/images/generate-catalog-service-proxy.png b/docs/en/tutorials/microservice/images/generate-catalog-service-proxy.png new file mode 100644 index 0000000000..a04d308234 Binary files /dev/null and b/docs/en/tutorials/microservice/images/generate-catalog-service-proxy.png differ diff --git a/docs/en/tutorials/microservice/images/generate-proxy-catalog-service.png b/docs/en/tutorials/microservice/images/generate-proxy-catalog-service.png new file mode 100644 index 0000000000..3b55d5b46e Binary files /dev/null and b/docs/en/tutorials/microservice/images/generate-proxy-catalog-service.png differ diff --git a/docs/en/tutorials/microservice/images/import-module-dialog.png b/docs/en/tutorials/microservice/images/import-module-dialog.png new file mode 100644 index 0000000000..b3739a650a Binary files /dev/null and b/docs/en/tutorials/microservice/images/import-module-dialog.png differ diff --git a/docs/en/tutorials/microservice/images/import-module.png b/docs/en/tutorials/microservice/images/import-module.png new file mode 100644 index 0000000000..596e8fb7e0 Binary files /dev/null and b/docs/en/tutorials/microservice/images/import-module.png differ diff --git a/docs/en/tutorials/microservice/images/install-module-dialog.png b/docs/en/tutorials/microservice/images/install-module-dialog.png new file mode 100644 index 0000000000..dbe8be1e10 Binary files /dev/null and b/docs/en/tutorials/microservice/images/install-module-dialog.png differ diff --git a/docs/en/tutorials/microservice/images/ordering-service-order-swagger-ui.png b/docs/en/tutorials/microservice/images/ordering-service-order-swagger-ui.png new file mode 100644 index 0000000000..98bb0f6a0f Binary files /dev/null and b/docs/en/tutorials/microservice/images/ordering-service-order-swagger-ui.png differ diff --git a/docs/en/tutorials/microservice/images/ordering-service-swagger-ui.png b/docs/en/tutorials/microservice/images/ordering-service-swagger-ui.png new file mode 100644 index 0000000000..cc97a9880c Binary files /dev/null and b/docs/en/tutorials/microservice/images/ordering-service-swagger-ui.png differ diff --git a/docs/en/tutorials/microservice/images/orders.png b/docs/en/tutorials/microservice/images/orders.png new file mode 100644 index 0000000000..81c9e16503 Binary files /dev/null and b/docs/en/tutorials/microservice/images/orders.png differ diff --git a/docs/en/tutorials/microservice/images/sql-server-management-studio-databases-2.png b/docs/en/tutorials/microservice/images/sql-server-management-studio-databases-2.png new file mode 100644 index 0000000000..6bbbfcd4b1 Binary files /dev/null and b/docs/en/tutorials/microservice/images/sql-server-management-studio-databases-2.png differ diff --git a/docs/en/tutorials/microservice/images/sql-server-management-studio-databases.png b/docs/en/tutorials/microservice/images/sql-server-management-studio-databases.png new file mode 100644 index 0000000000..c939789bc5 Binary files /dev/null and b/docs/en/tutorials/microservice/images/sql-server-management-studio-databases.png differ diff --git a/docs/en/tutorials/microservice/images/sql-server-management-studio-login-screen.png b/docs/en/tutorials/microservice/images/sql-server-management-studio-login-screen.png new file mode 100644 index 0000000000..c362ecd5d0 Binary files /dev/null and b/docs/en/tutorials/microservice/images/sql-server-management-studio-login-screen.png differ diff --git a/docs/en/tutorials/microservice/images/sql-server-management-studio-products.png b/docs/en/tutorials/microservice/images/sql-server-management-studio-products.png new file mode 100644 index 0000000000..f2f5f60040 Binary files /dev/null and b/docs/en/tutorials/microservice/images/sql-server-management-studio-products.png differ diff --git a/docs/en/tutorials/microservice/images/sql-server-orders-database-table-records.png b/docs/en/tutorials/microservice/images/sql-server-orders-database-table-records.png new file mode 100644 index 0000000000..12deb2efbd Binary files /dev/null and b/docs/en/tutorials/microservice/images/sql-server-orders-database-table-records.png differ diff --git a/docs/en/tutorials/microservice/images/visual-studio-new-migration-class.png b/docs/en/tutorials/microservice/images/visual-studio-new-migration-class.png new file mode 100644 index 0000000000..436af0ecae Binary files /dev/null and b/docs/en/tutorials/microservice/images/visual-studio-new-migration-class.png differ diff --git a/docs/en/tutorials/microservice/images/visual-studio-solution-explorer-catalog-service.png b/docs/en/tutorials/microservice/images/visual-studio-solution-explorer-catalog-service.png new file mode 100644 index 0000000000..86c47e0b89 Binary files /dev/null and b/docs/en/tutorials/microservice/images/visual-studio-solution-explorer-catalog-service.png differ diff --git a/docs/en/tutorials/microservice/images/vs-ordering-contracts.png b/docs/en/tutorials/microservice/images/vs-ordering-contracts.png new file mode 100644 index 0000000000..cd42258882 Binary files /dev/null and b/docs/en/tutorials/microservice/images/vs-ordering-contracts.png differ diff --git a/docs/en/tutorials/microservice/images/vs-ordering-entity.png b/docs/en/tutorials/microservice/images/vs-ordering-entity.png new file mode 100644 index 0000000000..eb93ed6083 Binary files /dev/null and b/docs/en/tutorials/microservice/images/vs-ordering-entity.png differ diff --git a/docs/en/tutorials/microservice/images/web-orders-page-with-product-name.png b/docs/en/tutorials/microservice/images/web-orders-page-with-product-name.png new file mode 100644 index 0000000000..391f0fa00c Binary files /dev/null and b/docs/en/tutorials/microservice/images/web-orders-page-with-product-name.png differ diff --git a/docs/en/tutorials/microservice/images/web-orders-page.png b/docs/en/tutorials/microservice/images/web-orders-page.png new file mode 100644 index 0000000000..9a60b7b942 Binary files /dev/null and b/docs/en/tutorials/microservice/images/web-orders-page.png differ diff --git a/docs/en/tutorials/microservice/index.md b/docs/en/tutorials/microservice/index.md index ee425f8afe..a4605f7508 100644 --- a/docs/en/tutorials/microservice/index.md +++ b/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) \ No newline at end of file diff --git a/docs/en/tutorials/microservice/part-01.md b/docs/en/tutorials/microservice/part-01.md new file mode 100644 index 0000000000..d843e744ca --- /dev/null +++ b/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). \ No newline at end of file diff --git a/docs/en/tutorials/microservice/part-02.md b/docs/en/tutorials/microservice/part-02.md new file mode 100644 index 0000000000..014daf9b11 --- /dev/null +++ b/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. \ No newline at end of file diff --git a/docs/en/tutorials/microservice/part-03.md b/docs/en/tutorials/microservice/part-03.md new file mode 100644 index 0000000000..31491ef004 --- /dev/null +++ b/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. \ No newline at end of file diff --git a/docs/en/tutorials/microservice/part-04.md b/docs/en/tutorials/microservice/part-04.md new file mode 100644 index 0000000000..50386ab8e1 --- /dev/null +++ b/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. \ No newline at end of file diff --git a/docs/en/tutorials/microservice/part-05.md b/docs/en/tutorials/microservice/part-05.md new file mode 100644 index 0000000000..249cdc8890 --- /dev/null +++ b/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 +{ + 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, + IHasEventInbox, + IHasEventOutbox +{ + public const string DbTablePrefix = ""; + public const string DbSchema = null; + + public const string DatabaseName = "OrderingService"; + + public DbSet IncomingEvents { get; set; } + public DbSet OutgoingEvents { get; set; } + public DbSet 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(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> 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 _orderRepository; + + public OrderAppService(IRepository orderRepository) + { + LocalizationResource = typeof(OrderingServiceResource); + _orderRepository = orderRepository; + } + + public async Task> GetListAsync() + { + var orders = await _orderRepository.GetListAsync(); + return ObjectMapper.Map, List>(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` 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(); + } +} +``` + +## 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 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 + +

Orders

+ + + + + @foreach (var order in Model.Orders) + { + + Customer: @order.CustomerName
+ Product: @order.ProductId
+ State: @order.State +
+ } +
+
+
+``` + +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. \ No newline at end of file diff --git a/docs/en/tutorials/microservice/part-06.md b/docs/en/tutorials/microservice/part-06.md new file mode 100644 index 0000000000..f171767260 --- /dev/null +++ b/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> GetProductsByIdsAsync(List 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 _productRepository; + + public ProductIntegrationService(IRepository productRepository) + { + LocalizationResource = typeof(CatalogServiceResource); + _productRepository = productRepository; + } + + public async Task> GetProductsByIdsAsync(List ids) + { + var products = await _productRepository.GetListAsync( + product => ids.Contains(product.Id) + ); + + return ObjectMapper.Map, List>(products); + } +} +``` + +`ProductIntegrationService` is a typical application service class that implements the `IProductIntegrationService` interface. It has a constructor that takes an `IRepository` object. This repository is used to fetch the product details from the database. + +> Here, we directly used `List` 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(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 _orderRepository; + private readonly IProductIntegrationService _productIntegrationService; + + public OrderAppService( + IRepository orderRepository, + IProductIntegrationService productIntegrationService) + { + LocalizationResource = typeof(OrderingServiceResource); + + _orderRepository = orderRepository; + _productIntegrationService = productIntegrationService; + } + + public async Task> 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>(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() + .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 + +

Orders

+ + + + + @foreach (var order in Model.Orders) + { + + Customer: @order.CustomerName
+ Product: @order.ProductName
+ State: @order.State +
+ } +
+
+
+``` + +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. diff --git a/docs/en/tutorials/microservice/part-07.md b/docs/en/tutorials/microservice/part-07.md new file mode 100644 index 0000000000..0351df2a52 --- /dev/null +++ b/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 _orderRepository; + private readonly IProductIntegrationService _productIntegrationService; + private readonly IDistributedEventBus _distributedEventBus; + + public OrderAppService( + IRepository orderRepository, + IProductIntegrationService productIntegrationService, + IDistributedEventBus distributedEventBus) + { + LocalizationResource = typeof(OrderingServiceResource); + + _orderRepository = orderRepository; + _productIntegrationService = productIntegrationService; + _distributedEventBus = distributedEventBus; + } + + public async Task> 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>(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, + ITransientDependency +{ + private readonly IRepository _productRepository; + + public OrderEventHandler(IRepository 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` 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. \ No newline at end of file diff --git a/docs/en/tutorials/modular-crm/part-02.md b/docs/en/tutorials/modular-crm/part-02.md index 6ebc22f20c..8a5b4b9e6c 100644 --- a/docs/en/tutorials/modular-crm/part-02.md +++ b/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