Tip
📘 This project documentation is also available in Spanish.
Evently API is an event management system built with .NET 10 using a Modular Monolith architecture and Event-Driven Design. It features secure OpenID Connect authentication via Keycloak, ensures reliable messaging through Outbox/Inbox patterns, and provides deep insights with OpenTelemetry. The infrastructure is fully containerized with Docker, leveraging PostgreSQL for persistence, Redis for distributed caching, and RabbitMQ as the message broker.
This project was built following Milan Jovanović's course Modular Monolith, using the latest ASP.NET Core features and best practices.
- .NET 10 – Modern framework for building scalable web APIs
- PostgreSQL – Open-source relational database
- Redis – In-memory data store used as a distributed cache
- KeyCloak – Open-source identity and access management
- RabbitMQ – Reliable and mature message broker for asynchronous communication between microservices
- MassTransit – Distributed application framework for .NET that simplifies working with message buses
- Yarp – A toolkit for building high-performance HTTP proxy servers using .NET infrastructure
- Dapper – High-performance micro-ORM for .NET used for efficient, raw SQL-based data access
- Entity Framework Core – ORM for data access for .NET
- Docker – Containerization and local infrastructure
- Swagger (Swashbuckle) – OpenAPI interactive documentation
- FluentValidation – Model validation framework
- Quartz.NET – Background jobs and scheduling
- OpenTelemetry – Distributed tracing and observability
- Seq – Optional centralized log aggregation
- Jaeger – Open-source, end-to-end distributed tracing system
- xUnit – Unit testing framework
- Testcontainers – Integration testing with containers
- SonarAnalyzer – Static code analysis
- PostgreSQL with EF Core, Dapper, and snake_case naming conventions
- Background jobs with Quartz
- Redis as a distributed cache and high-performance key-value store
- RabbitMQ for asynchronous messaging and service-to-service communication
- YARP (Yet Another Reverse Proxy) for routing and API Gateway functionality
- Interactive API docs with Swagger
- OpenID Connect (OIDC) identity provider integration with Keycloak
- Claims-based identity transformed from OIDC profile scopes
- Role-based Access Control (RBAC) through custom policies and roles
- OpenTelemetry-based distributed tracing
- Health checks support
- Logging with Seq
- Distributed tracing with Jaeger
- Unit testing with xUnit
- Architectural testing with NetArchTest
- Integration testing with Testcontainers, PostgreSQL, Redis, KeyCloak and RabbitMq
- Local development support with Docker Compose
- Centralized package versioning with MSBuild
- Dockerized production image support
Make sure you have .NET CLI installed on your system. You can check if it's available by running:
dotnet --versionThis should print the installed version of the .NET CLI. If it's not installed, download it from the official .NET site.
To verify which SDK versions are installed:
dotnet --list-sdksImportant
The minimum .NET SDK version required is 10.0.0
Additionally, the project uses Docker for running supporting services (e.g., PostgreSQL, Redis, RabbitMQ, Seq). You’ll need:
- Docker: Recommended to install Docker Desktop.
- Docker Compose: Typically included with Docker Desktop.
To check that Docker is installed and running:
docker --version
docker compose versionIf these commands fail or return errors, refer to the Docker installation guide.
To get started, clone the repository and set up the environment configuration:
- Clone the repository:
git clone https://github.com/jaimejaramilloperez/evently.git- Navigate to the project directory:
cd evently- Generate and trust the HTTPS development certificate:
dotnet dev-certs https -ep ./src/Evently.Api/certificate.pfx -p Test1234!
dotnet dev-certs https --trust- Copy the environment template and configure it:
cp .env.template .env
# Edit the .env file as neededAfter installation, you're ready to run the app either locally or using Docker. See the Local Development or Docker sections for details.
Set up your environment to run the Evently API either locally or with Docker, depending on your workflow.
Note
The configuration values shown (e.g., passwords, ports, keys, connection strings) are provided for demonstration purposes only. You are free to modify them as needed — especially for production environments.
Tip
If the APIs runs inside a Docker container, localhost refers to the container itself. In that case, replace localhost with the service name defined in your Docker network (e.g., evently.seq and evently.jaeger).
You can run the API locally using the .NET CLI and supporting services (PostgreSQL, Redis, RabbitMQ, Seq, etc.) via Docker Compose.
Important
Sensitive values should be stored securely using user secrets
- Review and update the following configuration files as needed:
- Evently.Api - appsettings.Development.json
- Evently.Ticketing.Api - appsettings.Development.json
- Evently.Gateway - appsettings.Development.json
- Evently.Api - modules.attendance.Development.json
- Evently.Api - modules.events.Development.json
- Evently.Api - modules.users.Development.json
- Evently.Ticketing.Api - modules.ticketing.Development.json
- Configure environment variables (
.envfile):
# postgresql
POSTGRES_DB="evently"
POSTGRES_USER="evently"
POSTGRES_PASSWORD="123456"
# redis
REDIS_PASSWORD="123456"
# pgadmin
PGADMIN_EMAIL="user@mail.com"
PGADMIN_PASSWORD="123456"
# seq
SEQ_SERVER_URL="http://evently.seq:5341"
SEQ_PASSWORD="12345678"
# keycloak
KEYCLOAK_USERNAME="admin"
KEYCLOAK_PASSWORD="admin"
# OpenTelemetry
OTEL_EXPORTER_OTLP_ENDPOINT="http://evently.jaeger:4317"
OTEL_EXPORTER_OTLP_PROTOCOL="grpc"
# RabbitMq
RABBITMQ_USER="guest"
RABBITMQ_PASSWORD="guest"- Start docker services:
docker compose up -d- Run the APIs and the gateway:
dotnet run --project src/Api/Evently.Api
dotnet run --project src/Api/Evently.Ticketing.Api
dotnet run --project src/Api/Evently.Gateway
# or with HTTPS
dotnet run --launch-profile https --project src/Api/Evently.Api
dotnet run --launch-profile https --project src/Api/Evently.Ticketing.Api
dotnet run --launch-profile https --project src/Api/Evently.GatewayThis mode runs both the application and services in Docker containers using a development image.
- Review and update the
.envfile:
# docker
COMPOSE_PROJECT_NAME="evently"
DOCKER_REGISTRY=""
DOCKER_IMAGE_TAG="dev"
DOCKER_BUILD_TARGET="dev"
# dotnet
BUILD_CONFIGURATION="Debug"
ENVIRONMENT="Development"
HTTP_PORT="5000"
HTTPS_PORT="5001"
CERTIFICATE_PATH="/https/certificate.pfx"
CERTIFICATE_PASSWORD="Test1234!"
# evently api
DATABASE_CONNECTION_STRING="Host=evently.database;Port=5432;Database=evently;Username=evently;Password=123456;"
CACHE_CONNECTION_STRING="evently.cache:6379,password=123456"
AUTHENTICATION_AUDIENCE="account"
AUTHENTICATION_VALID_ISSUERS_1="http://evently.identity:8080/realms/evently"
AUTHENTICATION_VALID_ISSUERS_2="http://host.docker.internal:18000/realms/evently"
AUTHENTICATION_METADATA_ADDRESS="http://evently.identity:8080/realms/evently/.well-known/openid-configuration"
AUTHENTICATION_REQUIRE_HTTPS_METADATA="false"
KEYCLOAK_HEALTH_Url="http://evently.identity:9000/health"
USERS_KEYCLOAK_ADMIN_URL="http://evently.identity:8080/admin/realms/evently/"
USERS_KEYCLOAK_TOKEN_URL="http://evently.identity:8080/realms/evently/protocol/openid-connect/token"
USERS_KEYCLOAK_CONFIDENTIAL_CLIENT_ID="evently-confidential-client"
USERS_KEYCLOAK_CONFIDENTIAL_CLIENT_SECRET="eHPYDdH5j8eutm54aApbgb4khT3vQXPM"
USERS_KEYCLOAK_PUBLIC_CLIENT_ID="evently-public-client"
# evently ticketing api
TICKETING_HTTP_PORT="5100"
TICKETING_HTTPS_PORT="5101"
TICKETING_CERTIFICATE_PATH="/https/certificate.pfx"
TICKETING_CERTIFICATE_PASSWORD="Test1234!"
# Gateway
GATEWAY_HTTP_PORT="3000"
GATEWAY_HTTPS_PORT="3001"
GATEWAY_CERTIFICATE_PATH="/https/certificate.pfx"
GATEWAY_CERTIFICATE_PASSWORD="Test1234!"
# postgresql
POSTGRES_DB="evently"
POSTGRES_USER="evently"
POSTGRES_PASSWORD="123456"
# redis
REDIS_PASSWORD="123456"
# pgadmin
PGADMIN_EMAIL="user@mail.com"
PGADMIN_PASSWORD="123456"
# seq
SEQ_SERVER_URL="http://evently.seq:5341"
SEQ_PASSWORD="12345678"
# keycloak
KEYCLOAK_USERNAME="admin"
KEYCLOAK_PASSWORD="admin"
# OpenTelemetry
OTEL_EXPORTER_OTLP_ENDPOINT="http://evently.jaeger:4317"
OTEL_EXPORTER_OTLP_PROTOCOL="grpc"
# RabbitMq
RABBITMQ_USER="guest"
RABBITMQ_PASSWORD="guest"- Start the containers:
docker compose -f ./docker-compose-debug.yml up -dImportant
Debugging inside containers requires the vsdbg debugger. If it’s not already installed, refer to the official setup guide for instructions on how to install it manually.
If you're using Visual Studio Code, you can debug the application running inside a container with the Containers .NET Attach option. This option attaches the debugger to a running container — no extra configuration required beyond starting the containers as shown above in the step 2 of docker for development.
To build and run the API in production mode using a minimal Docker image:
- Update the
.envfile with the required following values:
DOCKER_BUILD_TARGET="prod"
BUILD_CONFIGURATION="Release"
ENVIRONMENT="Production"- Run the
docker-compose-debug.ymlfile with docker again or build the images manually
docker buildx build \
--platform linux/amd64 \
-f src/Api/Evently.Api/Dockerfile \
--target prod \
-t evently.api:latest .docker buildx build \
--platform linux/amd64 \
-f src/Api/Evently.Ticketing.Api/Dockerfile \
--target prod \
-t evently.ticketing.api:latest .docker buildx build \
--platform linux/amd64 \
-f src/Api/Evently.Gateway/Dockerfile \
--target prod \
-t evently.gateway:latest .This project includes unit, integration and architectural tests.
Note
Tests are located in the root /test directory and the inside each module.
- xUnit: Test framework.
- NetArchTest.Rules: Architectural tests.
- Testcontainers: Integration testing with ephemeral container instances.
- Microsoft.AspNetCore.Mvc.Testing: End-to-end and functional tests.
dotnet testEvently API provides interactive documentation via Swagger with support for versioned endpoints and JWT authentication.
Once the APIs are running:
- OpenAPI spec (JSON):
https://localhost:5001/openapi/v1.json - Swagger UI:
https://localhost:5001/swagger
- OpenAPI spec (JSON):
https://localhost:5101/openapi/v1.json - Swagger UI:
https://localhost:5101/swagger
Note
Replace 5001 and 5101 with your actual HTTPS ports if different.
