mirror of
https://github.com/meysamhadeli/booking-microservices.git
synced 2026-04-10 17:59:38 +08:00
Merge pull request #357 from meysamhadeli/docs/update-aspir-docs
docs/update aspir docs
This commit is contained in:
commit
44e408258f
120
README.md
120
README.md
@ -1,23 +1,20 @@
|
||||
# 🛩️ Booking Monolith
|
||||
|
||||
<div align="center" style="margin-bottom:20px">
|
||||
<img src="assets/logo.png" alt="booking-microservices" />
|
||||
<div align="center">
|
||||
<a href="https://github.com/meysamhadeli/booking-microservices/actions/workflows/ci.yml"><img alt="ci-status" src="https://github.com/meysamhadeli/booking-microservices/actions/workflows/ci.yml/badge.svg?branch=main&style=flat-square"/></a>
|
||||
<a href="https://github.com/meysamhadeli/booking-microservices/blob/main/LICENSE"><img alt="build-status" src="https://img.shields.io/github/license/meysamhadeli/booking-microservices?color=%234275f5&style=flat-square"/></a>
|
||||
<div align="left">
|
||||
<a href="https://github.com/meysamhadeli/booking-monolith/actions/workflows/ci.yml"><img alt="build-status" src="https://github.com/meysamhadeli/booking-monolith/actions/workflows/ci.yml/badge.svg?branch=main&style=flat-square"/></a>
|
||||
<a href="https://github.com/meysamhadeli/booking-monolith/blob/main/LICENSE"><img alt="build-status" src="https://img.shields.io/github/license/meysamhadeli/booking-monolith?color=%234275f5&style=flat-square"/></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
> 🚀 **A practical microservices with the latest technologies and architecture like Vertical Slice Architecture, Event Sourcing, CQRS, DDD, gRpc, MongoDB, RabbitMq and Masstransit in .Net 9.**
|
||||
> 🚀 **A practical Monolith architecture with the latest technologies and architectures like Vertical Slice Architecture, Event Driven Architecture, CQRS, DDD and Aspire in .Net 9.**
|
||||
|
||||
## You can find other version of this project here:
|
||||
- [Booking with Microservices Architecture](https://github.com/meysamhadeli/booking-microservices)
|
||||
- [Booking with Modular Monolith Architecture](https://github.com/meysamhadeli/booking-modular-monolith)
|
||||
- [Booking with Monolith Architecture](https://github.com/meysamhadeli/booking-monolith)
|
||||
|
||||
<div>
|
||||
<a href="https://gitpod.io/#https://github.com/meysamhadeli/booking-microservices"><img alt="Open in Gitpod" src="https://gitpod.io/button/open-in-gitpod.svg"/></a>
|
||||
</div>
|
||||
<div>
|
||||
<a href='https://codespaces.new/meysamhadeli/booking-microservices?quickstart=1'><img alt='Open in GitHub Codespaces' src='https://github.com/codespaces/badge.svg'></a>
|
||||
</div>
|
||||
<a href="https://gitpod.io/#https://github.com/meysamhadeli/booking-monolith"><img alt="Open in Gitpod" src="https://gitpod.io/button/open-in-gitpod.svg"/></a>
|
||||
|
||||
|
||||
# Table of Contents
|
||||
|
||||
@ -26,17 +23,15 @@
|
||||
- [Key Features](#key-features)
|
||||
- [When to Use](#when-to-use)
|
||||
- [Challenges](#challenges)
|
||||
- [The Domain and Bounded Context - Service Boundary](#the-domain-and-bounded-context---service-boundary)
|
||||
- [The Domain and Bounded Context](#the-domain-and-bounded-context)
|
||||
- [Structure of Project](#structure-of-project)
|
||||
- [Development Setup](#development-setup)
|
||||
- [Dotnet Tools Packages](#dotnet-tools-packages)
|
||||
- [Husky](#husky)
|
||||
- [Upgrade Nuget Packages](#upgrade-nuget-packages)
|
||||
- [Dotnet Tools Packages](#dotnet-tools-packages)
|
||||
- [Husky](#husky)
|
||||
- [Upgrade Nuget Packages](#upgrade-nuget-packages)
|
||||
- [How to Run](#how-to-run)
|
||||
- [Config Certificate](#config-certificate)
|
||||
- [Aspire](#aspire)
|
||||
- [Docker Compose](#docker-compose)
|
||||
- [Kubernetes](#kubernetes)
|
||||
- [Build](#build)
|
||||
- [Run](#run)
|
||||
- [Test](#test)
|
||||
@ -49,12 +44,11 @@
|
||||
|
||||
- :sparkle: Using `Vertical Slice Architecture` for `architecture` level.
|
||||
- :sparkle: Using `Domain Driven Design (DDD)` to implement all `business logic`.
|
||||
- :sparkle: Using `Rabbitmq` on top of `Masstransit` for `Event Driven Architecture`.
|
||||
- :sparkle: Using `gRPC` for `internal communication`.
|
||||
- :sparkle: Using `CQRS` implementation with `MediatR` library.
|
||||
- :sparkle: Using `Postgres` for `write side` database.
|
||||
- :sparkle: Using `InMemory Broker` on top of `Masstransit` for `Event Driven Architecture`.
|
||||
- :sparkle: Using `MongoDB` for `read side` database.
|
||||
- :sparkle: Using `Event Store` for `write side` of Booking Microservice/Module to store all `historical change` of aggregate.
|
||||
- :sparkle: Using `Event Store` for `write side` of Booking to store all `historical change` of aggregate.
|
||||
- :sparkle: Using `Inbox Pattern` for ensuring message idempotency for receiver and `Exactly once Delivery`.
|
||||
- :sparkle: Using `Outbox Pattern` for ensuring no message is lost and there is at `At Least One Delivery`.
|
||||
- :sparkle: Using `Unit Testing` for testing small units and mocking our dependencies with `Nsubstitute`.
|
||||
@ -68,11 +62,7 @@
|
||||
- :sparkle: Using `OpenTelemetry` for distributed tracing on top of `Jaeger`.
|
||||
- :sparkle: Using `OpenTelemetry` for monitoring on top of `Prometheus` and `Grafana`.
|
||||
- :sparkle: Using `IdentityServer` for authentication and authorization base on `OpenID-Connect` and `OAuth2`.
|
||||
- :sparkle: Using `Yarp` as a microservices `gateway`.
|
||||
- :sparkle: Using `Kubernetes` to achieve efficient `scaling` and ensure `high availability` for each of our microservices.
|
||||
- :sparkle: Using `Nginx Ingress Controller` for `load balancing` between our microservices top of `Kubernetes`.
|
||||
- :sparkle: Using `cert-manager` to Configure `TLS` in `kubernetes cluster`.
|
||||
|
||||
- :sparkle: Using `Aspire` for `service discovery`, `observability`, and `local orchestration` of microservices.
|
||||
|
||||
## Technologies - Libraries
|
||||
|
||||
@ -95,48 +85,46 @@
|
||||
- ✔️ **[`Hellang.Middleware.ProblemDetails`](https://github.com/khellang/Middleware/tree/master/src/ProblemDetails)** - A middleware for handling exception in .Net Core.
|
||||
- ✔️ **[`NewId`](https://github.com/phatboyg/NewId)** - NewId can be used as an embedded unique ID generator that produces 128 bit (16 bytes) sequential IDs.
|
||||
- ✔️ **[`Yarp`](https://github.com/microsoft/reverse-proxy)** - Reverse proxy toolkit for building fast proxy servers in .NET.
|
||||
- ✔️ **[`Tye`](https://github.com/dotnet/tye)** - Developer tool that makes developing, testing, and deploying microservices and distributed applications easier.
|
||||
- ✔️ **[`gRPC-dotnet`](https://github.com/grpc/grpc-dotnet)** - gRPC functionality for .NET.
|
||||
- ✔️ **[`EventStore`](https://github.com/EventStore/EventStore)** - The open-source, functional database with Complex Event Processing.
|
||||
- ✔️ **[`MongoDB.Driver`](https://github.com/mongodb/mongo-csharp-driver)** - .NET Driver for MongoDB.
|
||||
- ✔️ **[`xUnit.net`](https://github.com/xunit/xunit)** - A free, open source, community-focused unit testing tool for the .NET Framework.
|
||||
- ✔️ **[`Respawn`](https://github.com/jbogard/Respawn)** - Respawn is a small utility to help in resetting test databases to a clean state.
|
||||
- ✔️ **[`Testcontainers`](https://github.com/testcontainers/testcontainers-dotnet)** - Testcontainers for .NET is a library to support tests with throwaway instances of Docker containers.
|
||||
- ✔️ **[`K6`](https://github.com/grafana/k6)** - Modern load testing for developers and testers in the DevOps era.
|
||||
- ✔️ **[`Aspire`](https://github.com/dotnet/aspire)** - .NET stack for building and orchestrating observable, distributed cloud-native applications.
|
||||
|
||||
|
||||
## Key Features
|
||||
1. **Independent Services**: Each service is a separate project with its own database and deployment pipeline, enabling independent development and deployment.
|
||||
2. **Decentralized Communication**: Services communicate via APIs (REST, gRPC) or message brokers (RabbitMQ, Kafka), ensuring loose coupling and resilience.
|
||||
3. **Scalability**: Services can be scaled independently based on demand, allowing efficient resource utilization.
|
||||
4. **Fault Tolerance**: Failures are isolated, preventing cascading failures and ensuring high availability.
|
||||
5. **Technology Agnostic**: Services can use different technologies, frameworks, or databases, providing flexibility.
|
||||
1. **Single Codebase**: All components (UI, business logic, data access) are part of one project.
|
||||
2. **Tight Coupling**: Components are highly dependent on each other, making changes riskier.
|
||||
3. **Simple Deployment**: The entire application is deployed as a single unit.
|
||||
4. **Centralized Database**: Typically uses a single database for all data storage and access.
|
||||
|
||||
|
||||
## When to Use
|
||||
1. **Large and Complex Projects**: Ideal for applications with complex business logic that can be broken into smaller, manageable services.
|
||||
2. **High Scalability Needs**: Suitable for applications requiring independent scaling of components.
|
||||
3. **Fault Tolerance and High Availability**: Perfect for systems where failure isolation and uptime are critical.
|
||||
4. **Distributed Teams**: Enables teams to work independently on different services.
|
||||
5. **Frequent Updates**: Supports continuous deployment and A/B testing for individual services.
|
||||
6. **Technology Diversity**: Allows the use of different technologies for different services.
|
||||
1. **Small to Medium Projects**: Ideal for applications with limited complexity and scope.
|
||||
2. **Rapid Development**: Suitable for projects requiring quick development and deployment.
|
||||
3. **Small Teams**: Works well for small teams with limited resources.
|
||||
4. **Low Scalability Needs**: Best for applications with predictable and low traffic.
|
||||
|
||||
|
||||
## Challenges
|
||||
- Increased complexity in management, DevOps overhead, data consistency, latency, and higher costs.
|
||||
- Harder to maintain as the codebase grows.
|
||||
- Limited scalability (scaling requires scaling the entire application).
|
||||
- Difficult to adopt new technologies incrementally.
|
||||
|
||||
|
||||
## The Domain And Bounded Context - Service Boundary
|
||||
## The Domain And Bounded Context
|
||||
|
||||
- `Identity Service`: The Identity Service is a bounded context for the authentication and authorization of users using [Identity Server](https://github.com/DuendeSoftware/IdentityServer). This service is responsible for creating new users and their corresponding roles and permissions using [.Net Core Identity](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity) and Jwt authentication and authorization.
|
||||
- `Identity`: The Identity is a bounded context for the authentication and authorization of users using [Identity Server](https://github.com/DuendeSoftware/IdentityServer). This service is responsible for creating new users and their corresponding roles and permissions using [.Net Core Identity](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity) and Jwt authentication and authorization.
|
||||
|
||||
- `Flight Service`: The Flight Service is a bounded context `CRUD` service to handle flight related operations.
|
||||
- `Flight`: The Flight is a bounded context `CRUD` service to handle flight related operations.
|
||||
|
||||
- `Passenger Service`: The Passenger Service is a bounded context for managing passenger information, tracking activities and subscribing to get notification for out of stock products.
|
||||
- `Passenger`: The Passenger is a bounded context for managing passenger information, tracking activities and subscribing to get notification for out of stock products.
|
||||
|
||||
- `Booking Service`: The Booking Service is a bounded context for managing all operation related to booking ticket.
|
||||
- `Booking`: The Booking is a bounded context for managing all operation related to booking ticket.
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
## Structure of Project
|
||||
@ -178,7 +166,7 @@ dotnet tool restore
|
||||
```
|
||||
|
||||
### Husky
|
||||
Here we use `husky` to handel some pre commit rules and we used `conventional commits` rules and `formatting` as pre commit rules, here in [package.json](.././package.json). of course, we can add more rules for pre commit in future. (find more about husky in the [documentation](https://typicode.github.io/husky/get-started.html))
|
||||
Here we use `husky` to handel some pre commit rules and we used `conventional commits` rules and `formatting` as pre commit rules, here in [package.json](./package.json). of course, we can add more rules for pre commit in future. (find more about husky in the [documentation](https://typicode.github.io/husky/get-started.html))
|
||||
We need to install `husky` package for `manage` `pre commits hooks` and also I add two packages `@commitlint/cli` and `@commitlint/config-conventional` for handling conventional commits rules in [package.json](.././package.json).
|
||||
Run the command bellow in the root of project to install all npm dependencies related to husky:
|
||||
|
||||
@ -205,24 +193,13 @@ Run the following commands to [Config SSL](https://docs.microsoft.com/en-us/aspn
|
||||
dotnet dev-certs https -ep %USERPROFILE%\.aspnet\https\aspnetapp.pfx -p password
|
||||
dotnet dev-certs https --trust
|
||||
```
|
||||
> Note: for running this command in `powershell` use `$env:USERPROFILE` instead of `%USERPROFILE%`*
|
||||
***Note:** for running this command in `powershell` use `$env:USERPROFILE` instead of `%USERPROFILE%`*
|
||||
|
||||
#### macOS or Linux
|
||||
```bash
|
||||
dotnet dev-certs https -ep ${HOME}/.aspnet/https/aspnetapp.pfx -p $CREDENTIAL_PLACEHOLDER$
|
||||
dotnet dev-certs https --trust
|
||||
```
|
||||
|
||||
### Aspire
|
||||
|
||||
To run the application using the `ASPIRE App Host`, execute the following command from the solution root:
|
||||
|
||||
```bash
|
||||
dotnet run --project ./src/Aspire/src/AppHost
|
||||
```
|
||||
|
||||
> Note:The `ASPIRE dashboard` will be available at `http://localhost:18888`
|
||||
|
||||
> ### Docker Compose
|
||||
|
||||
|
||||
@ -232,41 +209,28 @@ To run this app in `Docker`, use the [docker-compose.yaml](./deployments/docker-
|
||||
docker-compose -f ./deployments/docker-compose/docker-compose.yaml up -d
|
||||
```
|
||||
|
||||
> ### Kubernetes
|
||||
To `configure TLS` in the `Kubernetes cluster`, we need to install `cert-manager` based on the [docs](https://cert-manager.io/docs/installation) and run the following commands to apply TLS in our application. Here, we use [Let's Encrypt](https://letsencrypt.org/) to encrypt our certificate.
|
||||
|
||||
```bash
|
||||
kubectl apply -f ./deployments/kubernetes/booking-cert-manager.yml
|
||||
```
|
||||
|
||||
To apply all necessary `deployments`, `pods`, `services`, `ingress`, and `config maps`, please run the following command:
|
||||
|
||||
```bash
|
||||
kubectl apply -f ./deployments/kubernetes/booking-microservices.yml
|
||||
```
|
||||
|
||||
> ### Build
|
||||
To `build` all microservices, run this command in the `root` of the project:
|
||||
To `build` monolith app, run this command in the `root` of the project:
|
||||
```bash
|
||||
dotnet build
|
||||
```
|
||||
|
||||
> ### Run
|
||||
To `run` each microservice, run this command in the root of the `Api` folder of each microservice where the `csproj` file is located:
|
||||
To `run` monolith app, run this command in the root of the `Api` folder:
|
||||
```bash
|
||||
dotnet run
|
||||
```
|
||||
|
||||
> ### Test
|
||||
|
||||
To `test` all microservices, run this command in the `root` of the project:
|
||||
To `test` monolith app, run this command in the `root` of the project:
|
||||
```bash
|
||||
dotnet test
|
||||
```
|
||||
|
||||
> ### Documentation Apis
|
||||
|
||||
Each microservice provides `API documentation` and navigate to `/swagger` for `Swagger OpenAPI` or `/scalar/v1` for `Scalar OpenAPI` to visit list of endpoints.
|
||||
For checking `API documentation`, navigate to `/swagger` for `Swagger OpenAPI` or `/scalar/v1` for `Scalar OpenAPI` to visit list of endpoints.
|
||||
|
||||
As part of API testing, I created the [booking.rest](./booking.rest) file which can be run with the [REST Client](https://github.com/Huachao/vscode-restclient) `VSCode plugin`.
|
||||
|
||||
@ -280,7 +244,7 @@ Thanks a bunch for supporting me!
|
||||
|
||||
## Contribution
|
||||
|
||||
Thanks to all [contributors](https://github.com/meysamhadeli/booking-microservices/graphs/contributors), you're awesome and this wouldn't be possible without you! The goal is to build a categorized, community-driven collection of very well-known resources.
|
||||
Thanks to all [contributors](https://github.com/meysamhadeli/booking-monolith/graphs/contributors), you're awesome and this wouldn't be possible without you! The goal is to build a categorized, community-driven collection of very well-known resources.
|
||||
|
||||
Please follow this [contribution guideline](./CONTRIBUTION.md) to submit a pull request or create the issue.
|
||||
|
||||
@ -293,4 +257,4 @@ Please follow this [contribution guideline](./CONTRIBUTION.md) to submit a pull
|
||||
- [https://github.com/pdevito3/MessageBusTestingInMemHarness](https://github.com/pdevito3/MessageBusTestingInMemHarness)
|
||||
|
||||
## License
|
||||
This project is made available under the MIT license. See [LICENSE](https://github.com/meysamhadeli/booking-microservices/blob/main/LICENSE) for details.
|
||||
This project is made available under the MIT license. See [LICENSE](https://github.com/meysamhadeli/booking-monolith/blob/main/LICENSE) for details.
|
||||
|
||||
@ -29,4 +29,4 @@ app.UseEndpoints(endpoints =>
|
||||
|
||||
app.MapGet("/", x => x.Response.WriteAsync(appOptions.Name));
|
||||
|
||||
app.Run();
|
||||
app.Run();
|
||||
@ -213,4 +213,4 @@ var gateway = builder.AddProject<Projects.ApiGateway>("api-gateway")
|
||||
.WithHttpEndpoint(port: 5001, name: "gateway-http")
|
||||
.WithHttpsEndpoint(port: 5000, name: "gateway-https");
|
||||
|
||||
builder.Build().Run();
|
||||
builder.Build().Run();
|
||||
@ -47,4 +47,4 @@ public class CachingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest,
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,4 +4,4 @@ public interface ICacheRequest
|
||||
{
|
||||
string CacheKey { get; }
|
||||
DateTime? AbsoluteExpirationRelativeToNow { get; }
|
||||
}
|
||||
}
|
||||
@ -4,4 +4,4 @@ namespace BuildingBlocks.Caching
|
||||
{
|
||||
string CacheKey { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -36,4 +36,4 @@ namespace BuildingBlocks.Caching
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7,4 +7,4 @@ public static class IdentityConstant
|
||||
public const string Admin = "admin";
|
||||
public const string User = "user";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,4 +8,4 @@ public record FlightDeleted(Guid Id) : IIntegrationEvent;
|
||||
public record AircraftCreated(Guid Id) : IIntegrationEvent;
|
||||
public record AirportCreated(Guid Id) : IIntegrationEvent;
|
||||
public record SeatCreated(Guid Id) : IIntegrationEvent;
|
||||
public record SeatReserved(Guid Id) : IIntegrationEvent;
|
||||
public record SeatReserved(Guid Id) : IIntegrationEvent;
|
||||
@ -2,4 +2,4 @@ using BuildingBlocks.Core.Event;
|
||||
|
||||
namespace BuildingBlocks.Contracts.EventBus.Messages;
|
||||
|
||||
public record UserCreated(Guid Id, string Name, string PassportNumber) : IIntegrationEvent;
|
||||
public record UserCreated(Guid Id, string Name, string PassportNumber) : IIntegrationEvent;
|
||||
@ -3,4 +3,4 @@ using BuildingBlocks.Core.Event;
|
||||
namespace BuildingBlocks.Contracts.EventBus.Messages;
|
||||
|
||||
public record PassengerRegistrationCompleted(Guid Id) : IIntegrationEvent;
|
||||
public record PassengerCreated(Guid Id) : IIntegrationEvent;
|
||||
public record PassengerCreated(Guid Id) : IIntegrationEvent;
|
||||
@ -2,4 +2,4 @@ using BuildingBlocks.Core.Event;
|
||||
|
||||
namespace BuildingBlocks.Contracts.EventBus.Messages;
|
||||
|
||||
public record BookingCreated(Guid Id) : IIntegrationEvent;
|
||||
public record BookingCreated(Guid Id) : IIntegrationEvent;
|
||||
@ -9,4 +9,4 @@ public interface ICommand : ICommand<Unit>
|
||||
public interface ICommand<out T> : IRequest<T>
|
||||
where T : notnull
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -11,4 +11,4 @@ public interface ICommandHandler<in TCommand, TResponse> : IRequestHandler<TComm
|
||||
where TCommand : ICommand<TResponse>
|
||||
where TResponse : notnull
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -5,4 +5,4 @@ namespace BuildingBlocks.Core.CQRS;
|
||||
public interface IQuery<out T> : IRequest<T>
|
||||
where T : notnull
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -6,4 +6,4 @@ public interface IQueryHandler<in TQuery, TResponse> : IRequestHandler<TQuery, T
|
||||
where TQuery : IQuery<TResponse>
|
||||
where TResponse : notnull
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -34,4 +34,4 @@ public class CompositeEventMapper : IEventMapper
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,4 +6,4 @@ public enum EventType
|
||||
DomainEvent = 1,
|
||||
IntegrationEvent = 2,
|
||||
InternalCommand = 4
|
||||
}
|
||||
}
|
||||
@ -2,4 +2,4 @@ namespace BuildingBlocks.Core.Event;
|
||||
|
||||
public interface IDomainEvent : IEvent
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -9,4 +9,4 @@ public interface IEvent : INotification
|
||||
Guid EventId => NewId.NextGuid();
|
||||
public DateTime OccurredOn => DateTime.Now;
|
||||
public string EventType => GetType().AssemblyQualifiedName;
|
||||
}
|
||||
}
|
||||
@ -2,4 +2,4 @@ namespace BuildingBlocks.Core.Event;
|
||||
|
||||
public interface IHaveIntegrationEvent
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -5,4 +5,4 @@ namespace BuildingBlocks.Core.Event;
|
||||
[ExcludeFromTopology]
|
||||
public interface IIntegrationEvent : IEvent
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -2,4 +2,4 @@ namespace BuildingBlocks.Core.Event;
|
||||
|
||||
public interface IInternalCommand : IEvent
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -2,4 +2,4 @@ using BuildingBlocks.Core.CQRS;
|
||||
|
||||
namespace BuildingBlocks.Core.Event;
|
||||
|
||||
public record InternalCommand : IInternalCommand, ICommand;
|
||||
public record InternalCommand : IInternalCommand, ICommand;
|
||||
@ -23,4 +23,4 @@ public class MessageEnvelope<TMessage> : MessageEnvelope
|
||||
}
|
||||
|
||||
public new TMessage? Message { get; }
|
||||
}
|
||||
}
|
||||
@ -152,4 +152,4 @@ public sealed class EventDispatcher(
|
||||
|
||||
return headers;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,4 +8,4 @@ public interface IEventDispatcher
|
||||
where T : IEvent;
|
||||
public Task SendAsync<T>(T @event, Type type = null, CancellationToken cancellationToken = default)
|
||||
where T : IEvent;
|
||||
}
|
||||
}
|
||||
@ -6,4 +6,4 @@ public interface IEventMapper
|
||||
{
|
||||
IIntegrationEvent? MapToIntegrationEvent(IDomainEvent @event);
|
||||
IInternalCommand? MapToInternalCommand(IDomainEvent @event);
|
||||
}
|
||||
}
|
||||
@ -3,4 +3,4 @@ using BuildingBlocks.Core.Event;
|
||||
namespace BuildingBlocks.Core;
|
||||
|
||||
public record IntegrationEventWrapper<TDomainEventType>(TDomainEventType DomainEvent) : IIntegrationEvent
|
||||
where TDomainEventType : IDomainEvent;
|
||||
where TDomainEventType : IDomainEvent;
|
||||
@ -20,4 +20,4 @@ public abstract record Aggregate<TId> : Entity<TId>, IAggregate<TId>
|
||||
|
||||
return dequeuedEvents;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,4 +9,4 @@ public abstract record Entity<T> : IEntity<T>
|
||||
public long? LastModifiedBy { get; set; }
|
||||
public bool IsDeleted { get; set; }
|
||||
public long Version { get; set; }
|
||||
}
|
||||
}
|
||||
@ -10,4 +10,4 @@ public interface IAggregate : IEntity
|
||||
{
|
||||
IReadOnlyList<IDomainEvent> DomainEvents { get; }
|
||||
IEvent[] ClearDomainEvents();
|
||||
}
|
||||
}
|
||||
@ -12,4 +12,4 @@ public interface IEntity : IVersion
|
||||
public DateTime? LastModified { get; set; }
|
||||
public long? LastModifiedBy { get; set; }
|
||||
public bool IsDeleted { get; set; }
|
||||
}
|
||||
}
|
||||
@ -4,4 +4,4 @@ namespace BuildingBlocks.Core.Model;
|
||||
public interface IVersion
|
||||
{
|
||||
long Version { get; set; }
|
||||
}
|
||||
}
|
||||
@ -33,4 +33,4 @@ public static class Extensions
|
||||
|
||||
return PageList<TEntity>.Create(items.AsReadOnly(), pageRequest.PageNumber, pageRequest.PageSize, total);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -13,4 +13,4 @@ public interface IPageList<T>
|
||||
int TotalCount { get; init; }
|
||||
int PageNumber { get; init; }
|
||||
int PageSize { get; init; }
|
||||
}
|
||||
}
|
||||
@ -4,4 +4,4 @@ using MediatR;
|
||||
|
||||
public interface IPageQuery<out TResponse> : IPageRequest, IRequest<TResponse>
|
||||
where TResponse : class
|
||||
{ }
|
||||
{ }
|
||||
@ -6,4 +6,4 @@ public interface IPageRequest
|
||||
int PageSize { get; init; }
|
||||
string? Filters { get; init; }
|
||||
string? SortOrder { get; init; }
|
||||
}
|
||||
}
|
||||
@ -16,4 +16,4 @@ public record PageList<T>(IReadOnlyList<T> Items, int PageNumber, int PageSize,
|
||||
{
|
||||
return new PageList<T>(items, pageNumber, pageSize, totalItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -178,4 +178,4 @@ public abstract class AppDbContextBase : DbContext, IDbContext
|
||||
throw new System.Exception("try for find IAggregate", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -57,4 +57,4 @@ namespace BuildingBlocks.EFCore
|
||||
return CreateNewInstance(options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -78,4 +78,4 @@ where TResponse : notnull
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -138,4 +138,4 @@ public static class Extensions
|
||||
|
||||
await seedersManager.ExecuteSeedAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,4 +9,4 @@ namespace BuildingBlocks.EFCore
|
||||
{
|
||||
Task SeedAllAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -15,4 +15,4 @@ public interface IDbContext
|
||||
Task RollbackTransactionAsync(CancellationToken cancellationToken = default);
|
||||
IExecutionStrategy CreateExecutionStrategy();
|
||||
Task ExecuteTransactionalAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
||||
@ -4,4 +4,4 @@ public interface ISeedManager
|
||||
{
|
||||
Task ExecuteSeedAsync();
|
||||
Task ExecuteTestSeedAsync();
|
||||
}
|
||||
}
|
||||
@ -3,4 +3,4 @@ namespace BuildingBlocks.EFCore;
|
||||
public class PostgresOptions
|
||||
{
|
||||
public string ConnectionString { get; set; }
|
||||
}
|
||||
}
|
||||
@ -39,4 +39,4 @@ public class SeedManager(
|
||||
logger.LogInformation("Seed {SeederName} is completed.", testSeeder.GetType().Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -25,4 +25,4 @@ public class BackgroundWorker : BackgroundService
|
||||
await perform(stoppingToken);
|
||||
logger.LogInformation("Background worker stopped");
|
||||
}, stoppingToken);
|
||||
}
|
||||
}
|
||||
@ -90,4 +90,4 @@ public static class EventStoreDBConfigExtensions
|
||||
.AsImplementedInterfaces()
|
||||
.WithTransientLifetime());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -24,5 +24,4 @@ namespace BuildingBlocks.EventStoreDB.Events
|
||||
|
||||
public virtual void When(object @event) { }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -36,4 +36,4 @@ public static class AggregateStreamExtensions
|
||||
|
||||
return aggregate;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -40,4 +40,4 @@ public class EventTypeMapper
|
||||
|
||||
return type;
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -12,4 +12,4 @@ namespace BuildingBlocks.EventStoreDB.Events
|
||||
public interface IAggregateEventSourcing<T> : IAggregateEventSourcing, IEntity<T>
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,4 +6,4 @@ namespace BuildingBlocks.EventStoreDB.Events;
|
||||
public interface IEventHandler<in TEvent> : INotificationHandler<TEvent>
|
||||
where TEvent : IEvent
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -4,4 +4,4 @@ namespace BuildingBlocks.EventStoreDB.Events;
|
||||
|
||||
public interface IExternalEvent : IEvent
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -3,4 +3,4 @@ namespace BuildingBlocks.EventStoreDB.Events;
|
||||
public interface IProjection
|
||||
{
|
||||
void When(object @event);
|
||||
}
|
||||
}
|
||||
@ -26,5 +26,4 @@ public class StreamEvent<T> : StreamEvent where T : notnull
|
||||
public StreamEvent(T data, EventMetadata metadata) : base(data, metadata)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -16,4 +16,4 @@ public static class StreamEventExtensions
|
||||
var type = typeof(StreamEvent<>).MakeGenericType(eventData.GetType());
|
||||
return (StreamEvent)Activator.CreateInstance(type, eventData, metaData)!;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -25,4 +25,4 @@ public class StreamNameMapper
|
||||
return $"{tenantPrefix}{streamType.Name}-{aggregateId}";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -23,4 +23,4 @@ public static class Extensions
|
||||
.AddEventStoreDB(configuration)
|
||||
.AddProjections(assembliesToScan);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7,4 +7,4 @@ public interface IProjectionProcessor
|
||||
{
|
||||
Task ProcessEventAsync<T>(StreamEvent<T> streamEvent, CancellationToken cancellationToken = default)
|
||||
where T : INotification;
|
||||
}
|
||||
}
|
||||
@ -9,4 +9,4 @@ public interface IProjectionPublisher
|
||||
where T : INotification;
|
||||
|
||||
Task PublishAsync(StreamEvent streamEvent, CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
||||
@ -36,4 +36,4 @@ public class ProjectionPublisher : IProjectionPublisher
|
||||
return (Task)method
|
||||
.Invoke(this, new object[] { streamEvent, cancellationToken })!;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -71,4 +71,4 @@ public class EventStoreDBRepository<T> : IEventStoreDBRepository<T> where T : cl
|
||||
return events
|
||||
.Select(EventStoreDBSerializer.ToJsonEventData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -30,4 +30,4 @@ public static class RepositoryExtensions
|
||||
|
||||
return await repository.Update(entity, expectedVersion, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -36,4 +36,4 @@ public static class EventStoreDBSerializer
|
||||
Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(@event)),
|
||||
Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new { }))
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -78,4 +78,4 @@ public static class JsonObjectContractProvider
|
||||
.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
||||
.OrderByDescending(e => e.GetParameters().Length)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
@ -12,4 +12,4 @@ public class NonDefaultConstructorContractResolver : DefaultContractResolver
|
||||
base.CreateConstructorParameters
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -65,4 +65,4 @@ public static class SerializationExtensions
|
||||
{
|
||||
return new StringContent(obj.ToJson(), Encoding.UTF8, "application/json");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -73,4 +73,4 @@ public class EventStoreDBSubscriptionCheckpointRepository : ISubscriptionCheckpo
|
||||
}
|
||||
|
||||
private static string GetCheckpointStreamName(string subscriptionId) => $"checkpoint_{subscriptionId}";
|
||||
}
|
||||
}
|
||||
@ -188,4 +188,4 @@ public class EventStoreDBSubscriptionToAll
|
||||
logger.LogInformation("Checkpoint event - ignoring");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,4 +5,4 @@ public interface ISubscriptionCheckpointRepository
|
||||
ValueTask<ulong?> Load(string subscriptionId, CancellationToken ct);
|
||||
|
||||
ValueTask Store(string subscriptionId, ulong position, CancellationToken ct);
|
||||
}
|
||||
}
|
||||
@ -17,4 +17,4 @@ public class InMemorySubscriptionCheckpointRepository : ISubscriptionCheckpointR
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10,4 +10,4 @@ public class AggregateNotFoundException : System.Exception
|
||||
{
|
||||
return new AggregateNotFoundException(typeof(T).Name, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11,4 +11,4 @@ public class AppException : CustomException
|
||||
public AppException(string message, System.Exception innerException, HttpStatusCode statusCode = HttpStatusCode.BadRequest, int? code = null) : base(message, innerException, statusCode, code)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10,4 +10,4 @@ namespace BuildingBlocks.Exception
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,4 +8,4 @@ namespace BuildingBlocks.Exception
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -34,4 +34,4 @@ public class CustomException : System.Exception
|
||||
public HttpStatusCode StatusCode { get; }
|
||||
|
||||
public int? Code { get; }
|
||||
}
|
||||
}
|
||||
@ -13,4 +13,4 @@ namespace SmartCharging.Infrastructure.Exceptions
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -20,4 +20,4 @@ public class GrpcExceptionInterceptor : Interceptor
|
||||
throw new RpcException(new Status(StatusCode.Internal, exception.Message));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,4 +14,4 @@ namespace BuildingBlocks.Exception
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,4 +8,4 @@ namespace BuildingBlocks.Exception
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,4 +9,4 @@ public class ProblemDetailsWithCode : ProblemDetails
|
||||
{
|
||||
[JsonPropertyName("code")]
|
||||
public int? Code { get; set; }
|
||||
}
|
||||
}
|
||||
@ -8,4 +8,4 @@ namespace BuildingBlocks.Exception
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -87,4 +87,4 @@ public static class Extensions
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,4 +3,4 @@ namespace BuildingBlocks.HealthCheck;
|
||||
public class HealthOptions
|
||||
{
|
||||
public bool Enabled { get; set; } = true;
|
||||
}
|
||||
}
|
||||
@ -21,4 +21,4 @@ public class AuthHeaderHandler : DelegatingHandler
|
||||
|
||||
return base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -74,4 +74,4 @@ namespace BuildingBlocks.Jwt
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -37,4 +37,4 @@ public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest,
|
||||
_logger.LogInformation("[{Prefix}] Handled {X-RequestData}", prefix, typeof(TRequest).Name);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -16,4 +16,4 @@ public static class Extensions
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -35,4 +35,4 @@ public class ConsumeFilter<T> : IFilter<ConsumeContext<T>>
|
||||
public void Probe(ProbeContext context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -117,4 +117,4 @@ public static class Extensions
|
||||
.Ignore<
|
||||
ValidationException>(); // don't retry if we have invalid data and message goes to _error queue masstransit
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7,4 +7,4 @@ public class RabbitMqOptions
|
||||
public string UserName { get; set; }
|
||||
public string Password { get; set; }
|
||||
public ushort? Port { get; set; }
|
||||
}
|
||||
}
|
||||
@ -4,4 +4,4 @@ public enum TransportType
|
||||
{
|
||||
RabbitMq,
|
||||
InMemory
|
||||
}
|
||||
}
|
||||
@ -49,4 +49,4 @@ namespace BuildingBlocks.Mongo
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10,4 +10,4 @@ public interface IMongoDbContext : IDisposable
|
||||
Task CommitTransactionAsync(CancellationToken cancellationToken = default);
|
||||
Task RollbackTransaction(CancellationToken cancellationToken = default);
|
||||
void AddCommand(Func<Task> func);
|
||||
}
|
||||
}
|
||||
@ -5,4 +5,4 @@ namespace BuildingBlocks.Mongo;
|
||||
public interface IMongoRepository<TEntity, in TId> : IRepository<TEntity, TId>
|
||||
where TEntity : class, IAggregate<TId>
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -2,4 +2,4 @@ namespace BuildingBlocks.Mongo;
|
||||
|
||||
public interface IMongoUnitOfWork<out TContext> : IUnitOfWork<TContext> where TContext : class, IMongoDbContext
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -46,4 +46,4 @@ public interface IRepository<TEntity, in TId> :
|
||||
public interface IRepository<TEntity> : IRepository<TEntity, long>
|
||||
where TEntity : class, IAggregate<long>
|
||||
{
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user