├── .gitignore ├── README.md ├── docker-compose.yml ├── docs └── projeto_aplicado_xp.pdf └── images ├── apm ├── 00_summary.png ├── 01_transactions.png ├── 02_databases.png └── 03_erros.png ├── app ├── 01.png ├── 02.png ├── 03.png ├── 04.png ├── 05.png └── 06.png ├── backend └── swagger.png ├── diagrams ├── blueprint.drawio.png ├── c4_model_container.png ├── cqrs.jpg ├── dotnet_layers.png └── layers.jpg ├── logos ├── favicon.ico ├── logo.png └── logo_full.png └── tracing ├── kibana.png └── kibana_received_request.png /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Logo](https://github.com/wesleycosta/orangotango/raw/main/images/logos/logo_full.png) 2 | 3 | **Orangotango** is a hotel reservation system that utilizes a modern and scalable microservices architecture. 4 | 5 | ## Summary 6 | 7 | - [Projects](#projects) 8 | - [Architecture](#architecture) 9 | - [Application Overview](#application-overview) 10 | - [How to Run with Docker](#how-to-run-with-docker) 11 | - [Observability](#observability) 12 | - [Logs and Tracing](#logs-and-tracing) 13 | - [How to Contribute](#how-to-contribute) 14 | - [Next Steps](#next-steps) 15 | - [License](#license) 16 | 17 | 18 | ## Projects 19 | 20 | Orangotango is composed of six main projects, each playing a specific role in the system's architecture: 21 | 22 | - [**orangotango-app**](https://github.com/wesleycosta/orangotango-app): A Single Page Application (SPA) built with Angular that communicates with the **orangotango-api-gateway**.[![Docker Hub](https://img.shields.io/badge/docker-hub-black.svg)](https://hub.docker.com/repository/docker/wesleycosta/orangotango-app) 23 | - [**orangotango-api-gateway**](https://github.com/wesleycosta/orangotango-api-gateway): API Gateway using Ocelot to centralize requests for rooms and reservations microservices into a single entry point. [![Docker Hub](https://img.shields.io/badge/docker-hub-black.svg)](https://hub.docker.com/repository/docker/wesleycosta/orangotango-api-gateway) 24 | - [**orangotango-rooms**](https://github.com/wesleycosta/orangotango-rooms): A microservice responsible for providing information about the available rooms at the hotel. [![Docker Hub](https://img.shields.io/badge/docker-hub-black.svg)](https://hub.docker.com/repository/docker/wesleycosta/orangotango-rooms) 25 | - [**orangotango-reservations**](https://github.com/wesleycosta/orangotango-reservations): A microservice responsible for managing the reservation process at the hotel. [![Docker Hub](https://img.shields.io/badge/docker-hub-black.svg)](https://hub.docker.com/repository/docker/wesleycosta/orangotango-reservations) 26 | - [**orangotango-notifications**](https://github.com/wesleycosta/orangotango-notifications): A microservice responsible for sending notifications. 27 | - [**orangotango-packages**](https://github.com/wesleycosta/orangotango-packages): Shared packages among the microservices, providing common functionalities, which are: 28 | - **`Orangotango.Core`**: The kernel package for microservices, encompassing abstractions for messaging, events, repositories, services, aggregations, and more. [![NuGet](https://img.shields.io/nuget/v/Orangotango.Core.svg)](https://www.nuget.org/packages/Orangotango.Core) 29 | 30 | - **`Orangotango.Api`**: This package provides APIs centralizing Swagger configuration, standardized response patterns, middleware for logging incoming requests and outgoing responses. [![NuGet](https://img.shields.io/nuget/v/Orangotango.Api.svg)](https://www.nuget.org/packages/Orangotango.Api) 31 | 32 | - **`Orangotango.Events`**: Contains all solution events, establishing contracts for easy integration via messaging topology, facilitating data replication across microservices. [![NuGet](https://img.shields.io/nuget/v/Orangotango.Events.svg)](https://www.nuget.org/packages/Orangotango.Events) 33 | 34 | - **`Orangotango.Infra`**: Includes infrastructure-related configurations such as Entity Framework contexts, ELK-based logging configuration, Messaging using Mass Transit and RabbitMQ, among others. [![NuGet](https://img.shields.io/nuget/v/Orangotango.Infra.svg)](https://www.nuget.org/packages/Orangotango.Infra) 35 | 36 | ## Architecture 37 | 38 | Below is a diagram of the application architecture, illustrating how the microservices communicate with each other: 39 | 40 | [![Blueprint](https://github.com/wesleycosta/orangotango/blob/main/images/diagrams/blueprint.drawio.png)](https://github.com/wesleycosta/orangotango/blob/main/images/diagrams/blueprint.drawio.png) 41 | 42 | Orangotango's microservices, implemented in .NET, are designed using a Hexagonal / Event-Driven Architecture (EDA). 43 | 44 | The system emphasizes responsibility separation, adhering to SOLID, Clean Code, and Domain-Driven Design (DDD) principles. The microservices are organized into distinct layers: 45 | 46 | ![Layers](https://github.com/wesleycosta/orangotango/raw/main/images/diagrams/layers.jpg) 47 | 48 | 1. **Presentation Layer**: Includes a Web API with controllers and input models (DTOs) defining endpoint contracts. 49 | 2. **Application Layer**: Consists of Command Handlers, Mappers, Results, and Services. 50 | 3. **Domain Layer**: Comprises Entities, Commands, Validations, and Repository Abstractions. 51 | 4. **Infrastructure Layer**: Implements Repositories, the EF Context, and a Messaging Bus. 52 | 53 | ### CQRS 54 | 55 | In the Orangotango project, CQRS is implemented by separating write commands and read queries: 56 | 57 | - **Write Commands (Upsert Command):** Insert or update operations start in the presentation layer (Web API), move through the application layer to apply business logic in the domain layer, and are then persisted in the database by the infrastructure layer. 58 | 59 | - **Read Queries (Query Command):** Read operations begin in the presentation layer and interact directly with the infrastructure. The flow is bidirectional, allowing data to be returned to the presentation layer for display to the user. 60 | 61 | ![CQRS](https://github.com/wesleycosta/orangotango/raw/main/images/diagrams/cqrs.jpg) 62 | 63 | ### Layers 64 | 65 | See below an example of implementation using the aforementioned layers in orangotango-rooms: 66 | ![DotnetLayers](https://github.com/wesleycosta/orangotango/blob/main/images/diagrams/dotnet_layers.png) 67 | 68 | ### Design Patterns and Principles 69 | 70 | - Hexagonal / Event-Driven Architecture (EDA). 71 | - CQRS (Command Query Responsibility Segregation), DDD, SOLID, and Clean Code principles. 72 | - Service Layer, Repository Pattern, Notification Pattern, and Unit Of Work. 73 | - Resilience best practices including Retry Pattern, Circuit Breaker, and Exponential Backoff. 74 | 75 | 76 | ### Swagger 77 | ![swagger](https://github.com/wesleycosta/orangotango/blob/main/images/backend/swagger.png) 78 | 79 | Feel free to contribute to this project. For more information, refer to the individual repositories of each project. 80 | 81 | ## Application Overview 82 | 83 | The frontend was developed using Angular and is based on the Modernize template (available at: [Modernize Angular Material Dashboard](https://adminmart.com/product/modernize-angular-material-dashboard/)). 84 | 85 | Below, you can find screenshots of the main screens in the system. 86 | 87 | #### Dasboard 88 | ![Dashboard](https://github.com/wesleycosta/orangotango/raw/main/images/app/01.png) 89 | 90 | #### Reservations 91 | ![Dashboard](https://github.com/wesleycosta/orangotango/raw/main/images/app/02.png) 92 | 93 | #### Upsert Reservations 94 | ![Dashboard](https://github.com/wesleycosta/orangotango/raw/main/images/app/03.png) 95 | 96 | #### Rooms 97 | ![Dashboard](https://github.com/wesleycosta/orangotango/raw/main/images/app/04.png) 98 | 99 | #### Upsert Rooms 100 | ![Dashboard](https://github.com/wesleycosta/orangotango/raw/main/images/app/05.png) 101 | 102 | #### Modal Deletion Confirmation 103 | ![Dashboard](https://github.com/wesleycosta/orangotango/raw/main/images/app/06.png) 104 | 105 | 106 | ## How to Run with Docker 107 | 108 | To run the project using Docker, you can use the `docker-compose.yml` file, which sets up all the necessary services for Orangotango. Follow the steps below: 109 | 110 | 1. **Make sure you have Docker and Docker Compose installed.** 111 | 112 | - To install Docker, follow the instructions [here](https://docs.docker.com/get-docker/). 113 | - To install Docker Compose, follow the instructions [here](https://docs.docker.com/compose/install/). 114 | 115 | 2. **Clone the Orangotango repository.** 116 | 117 | ```bash 118 | git clone https://github.com/wesleycosta/orangotango.git 119 | ``` 120 | 121 | 3. **Run Docker Compose.** 122 | 123 | In the root directory of the project, run the command below to start all the services defined in the `docker-compose.yml` file: 124 | 125 | ```bash 126 | docker-compose up 127 | ``` 128 | 129 | This will start the following services: 130 | - **Elasticsearch** 131 | - **Kibana** 132 | - **SQL Server** 133 | - **RabbitMQ** 134 | - **orangotango-rooms** 135 | - **orangotango-reservations** 136 | - **orangotango-api-gateway** 137 | - **orangotango-app** 138 | 139 | 4. **Access the services.** 140 | 141 | - **Web Application (SPA)**: [http://localhost:81](http://localhost:81) 142 | - **API Gateway**: [http://localhost:8080](http://localhost:8080) 143 | - **Rooms Service**: [http://localhost:8081/api-docs](http://localhost:8081/api-docs) 144 | - **Reservations Service**: [http://localhost:8082/api-docs](http://localhost:8082/api-docs) 145 | - **Elasticsearch**: [http://localhost:9200](http://localhost:9200) 146 | - **Kibana**: [http://localhost:5601](http://localhost:5601) 147 | - **RabbitMQ Management**: [http://localhost:15672](http://localhost:15672) 148 | 149 | 5. **Shut down the services.** 150 | 151 | To stop all the services, press `Ctrl+C` in the terminal where the services are running, and then run: 152 | 153 | ```bash 154 | docker-compose down 155 | ``` 156 | 157 | ### Observability 158 | 159 | ### Summary 160 | ![Summary](https://github.com/wesleycosta/orangotango/blob/main/images/apm/00_summary.png) 161 | 162 | ### Transactions 163 | ![Transactions](https://github.com/wesleycosta/orangotango/blob/main/images/apm/01_transactions.png) 164 | 165 | ### Databases 166 | ![Databases](https://github.com/wesleycosta/orangotango/blob/main/images/apm/02_databases.png) 167 | 168 | ### Errors 169 | ![Errors](https://github.com/wesleycosta/orangotango/blob/main/images/apm/03_erros.png) 170 | 171 | ## Logs and Tracing 172 | 173 | To monitor communication between microservices, a `TraceId` field has been added to logs. This field enables tracking all stages of a specific operation, facilitating comprehensive tracing of its journey. 174 | 175 | ### Operation Example 176 | 177 | In the following example, a new room named `Master 01` is created in the **rooms** microservice. Using the `TraceId`, we can trace every step of this process, from the initial request to the final processing in the **reservations** microservice. 178 | 179 | ### Kibana Logs 180 | 181 | Below is the sequence of logs captured in Kibana for this operation: 182 | 183 | [![Blueprint](https://github.com/wesleycosta/orangotango/blob/main/images/tracing/kibana.png)](https://github.com/wesleycosta/orangotango/blob/main/images/tracing/kibana.png) 184 | 185 | [![Blueprint](https://github.com/wesleycosta/orangotango/blob/main/images/tracing/kibana_received_request.png)](https://github.com/wesleycosta/orangotango/blob/main/images/tracing/kibana_received_request) 186 | 187 | ### Explanation of Logs 188 | 189 | 1. **ReceivedRequest** (orangotango-rooms, 15:55:13.090): The **rooms** microservice receives the request to create a new room. 190 | 2. **EventPublished** (orangotango-rooms, 15:55:13.249): After creating the room, the `RoomUpsertedEvent` is published by the **rooms** microservice. 191 | 3. **ReturnedResponse** (orangotango-rooms, 15:55:13.251): The **rooms** microservice returns the response to the POST request. 192 | 4. **EventReceived** (orangotango-reservations, 15:55:13.336): The **reservations** microservice receives the `RoomUpsertedEvent`. 193 | 5. **EventProcessedSuccessfully** (orangotango-reservations, 15:55:13.379): The **reservations** microservice successfully processes the `RoomUpsertedEvent`. 194 | 195 | ### Using TraceId 196 | 197 | The `TraceId` field in each log entry allows correlating these events, making it easier to visualize the complete flow of an operation across different microservices. 198 | 199 | This detailed logging and tracing mechanism is crucial for debugging and monitoring the system, ensuring all operations can be audited and analyzed in case of failures or for future optimizations. 200 | 201 | ## How to Contribute 202 | 203 | 1. Fork the project 204 | 2. Create a branch for your feature (`git checkout -b feature/new-feature`) 205 | 3. Commit your changes (`git commit -m 'Add new feature'`) 206 | 4. Push to the branch (`git push origin feature/new-feature`) 207 | 5. Open a Pull Request 208 | 209 | ## Next Steps 210 | - **Complete Microservices Development and Unit Tests**: Finalize the development of current microservices and create unit tests. 211 | - **Kubernetes Deployment**: Deploy microservices on Kubernetes for high availability, scalability, and ease of management. 212 | - **CI/CD Enhancement**: Expand CI/CD pipelines with GitHub Actions to include additional deployment steps and code quality monitoring. 213 | - **Improve Application Resilience**: Implement Circuit Breaker and Retry Pattern to enhance robustness and handle temporary failures effectively. 214 | 215 | ## License 216 | 217 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 218 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | 3 | services: 4 | elasticsearch: 5 | image: docker.elastic.co/elasticsearch/elasticsearch:7.11.1 6 | ports: 7 | - 9200:9200 8 | - 9300:9300 9 | networks: 10 | - orangotango 11 | environment: 12 | discovery.type: single-node 13 | ES_JAVA_OPTS: "-Xms1g -Xmx1g" 14 | 15 | kibana: 16 | image: docker.elastic.co/kibana/kibana:7.11.1 17 | ports: 18 | - "5601:5601" 19 | networks: 20 | - orangotango 21 | depends_on: 22 | - elasticsearch 23 | 24 | sqlserver: 25 | image: mcr.microsoft.com/mssql/server:2019-latest 26 | ports: 27 | - "1432:1433" 28 | volumes: 29 | - mssql2019:/var/opt/mssql 30 | environment: 31 | SA_PASSWORD: "sqlserver@123" 32 | ACCEPT_EULA: "Y" 33 | networks: 34 | - orangotango 35 | 36 | rabbitmq: 37 | image: rabbitmq:3-management 38 | ports: 39 | - "5672:5672" 40 | - "15672:15672" 41 | networks: 42 | - orangotango 43 | 44 | orangotango-rooms: 45 | depends_on: 46 | - elasticsearch 47 | - sqlserver 48 | - rabbitmq 49 | image: wesleycosta/orangotango-rooms 50 | ports: 51 | - "8081:8080" 52 | networks: 53 | - orangotango 54 | environment: 55 | - ConnectionStrings__DefaultConnection=Server=sqlserver,1433;Database=rooms;User Id=sa;Password=sqlserver@123;TrustServerCertificate=True; 56 | - Elasticsearch__Uri=http://elasticsearch:9200 57 | - Elasticsearch__IndexFormat=orangotango-rooms-{0:yyyy.MM} 58 | - RabbitMQ__ConnectionString=amqp://guest:guest@rabbitmq:5672 59 | - NEW_RELIC_LICENSE_KEY= 60 | 61 | orangotango-reservations: 62 | depends_on: 63 | - elasticsearch 64 | - sqlserver 65 | - rabbitmq 66 | image: wesleycosta/orangotango-reservations 67 | ports: 68 | - "8082:8080" 69 | networks: 70 | - orangotango 71 | environment: 72 | - ConnectionStrings__DefaultConnection=Server=sqlserver,1433;Database=reservations;User Id=sa;Password=sqlserver@123;TrustServerCertificate=True; 73 | - Elasticsearch__Uri=http://elasticsearch:9200 74 | - Elasticsearch__IndexFormat=orangotango-reservations-{0:yyyy.MM} 75 | - RabbitMQ__ConnectionString=amqp://guest:guest@rabbitmq:5672 76 | - NEW_RELIC_LICENSE_KEY= 77 | 78 | orangotango-api-gateway: 79 | depends_on: 80 | - elasticsearch 81 | image: wesleycosta/orangotango-api-gateway 82 | ports: 83 | - "8080:8080" 84 | networks: 85 | - orangotango 86 | environment: 87 | - Elasticsearch__Uri=http://elasticsearch:9200 88 | - Elasticsearch__IndexFormat=orangotango-api-gateway-{0:yyyy.MM} 89 | - NEW_RELIC_LICENSE_KEY= 90 | 91 | orangotango-app: 92 | image: wesleycosta/orangotango-app 93 | ports: 94 | - "81:80" 95 | networks: 96 | - orangotango 97 | 98 | volumes: 99 | mssql2019: 100 | driver: local 101 | 102 | networks: 103 | orangotango: 104 | driver: bridge -------------------------------------------------------------------------------- /docs/projeto_aplicado_xp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/docs/projeto_aplicado_xp.pdf -------------------------------------------------------------------------------- /images/apm/00_summary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/apm/00_summary.png -------------------------------------------------------------------------------- /images/apm/01_transactions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/apm/01_transactions.png -------------------------------------------------------------------------------- /images/apm/02_databases.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/apm/02_databases.png -------------------------------------------------------------------------------- /images/apm/03_erros.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/apm/03_erros.png -------------------------------------------------------------------------------- /images/app/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/app/01.png -------------------------------------------------------------------------------- /images/app/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/app/02.png -------------------------------------------------------------------------------- /images/app/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/app/03.png -------------------------------------------------------------------------------- /images/app/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/app/04.png -------------------------------------------------------------------------------- /images/app/05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/app/05.png -------------------------------------------------------------------------------- /images/app/06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/app/06.png -------------------------------------------------------------------------------- /images/backend/swagger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/backend/swagger.png -------------------------------------------------------------------------------- /images/diagrams/blueprint.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/diagrams/blueprint.drawio.png -------------------------------------------------------------------------------- /images/diagrams/c4_model_container.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/diagrams/c4_model_container.png -------------------------------------------------------------------------------- /images/diagrams/cqrs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/diagrams/cqrs.jpg -------------------------------------------------------------------------------- /images/diagrams/dotnet_layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/diagrams/dotnet_layers.png -------------------------------------------------------------------------------- /images/diagrams/layers.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/diagrams/layers.jpg -------------------------------------------------------------------------------- /images/logos/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/logos/favicon.ico -------------------------------------------------------------------------------- /images/logos/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/logos/logo.png -------------------------------------------------------------------------------- /images/logos/logo_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/logos/logo_full.png -------------------------------------------------------------------------------- /images/tracing/kibana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/tracing/kibana.png -------------------------------------------------------------------------------- /images/tracing/kibana_received_request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesleycosta/orangotango/c91ab721e4b41d005b25a05060df5221a3be6363/images/tracing/kibana_received_request.png --------------------------------------------------------------------------------