A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://github.com/minhsangdotcom/clean-architecture below:

minhsangdotcom/clean-architecture: Clean Architecture Solution Template is for .NET CoreπŸš€.

Clean Architecture The Template

English | Vietnamese

Clean Architecture template is designed for backend developer working with ASP.NET Core. It provides you an efficient way to build enterprise applications effortlessly by leveraging advantages of clean architecture structure and .NET Core framework.

With this template, everything is already set up πŸ˜ƒ.

If you find this template helpful and learn something from it, please consider giving it a ⭐.

4. What is Clean Architecture?

Clean Architecture is a software design approach introduced by Robert C. Martin (Uncle Bob) that emphasizes the separation of concerns by organizing code into concentric layers. The core idea is to keep business logic independent from external frameworks, databases, and user interfaces, promoting a system that's easier to maintain, test, and evolve over time.

What makes this Clean Architecture template stand out from the rest on Github?

  1. DDD (Domain Driven Design) 🧠
  2. CQRS & Mediator πŸ”€
  3. Cross-cutting concern βœ‚οΈ
  4. Mail Sender πŸ“«
  5. Cached Repository πŸ’»
  6. Queue Example at feature/TicketSale 🚢
  7. Logging πŸ“
  8. Tracing πŸ“ˆ
  9. Automatical translatable messages 🌐
  10. S3 AWS ☁️

6.0.4. Automatic Translatable Message
{
  "type": "BadRequestError",
  "title": "Error has occured with password",
  "status": 400,
  "instance": "POST /api/v1/Users/Login",
  "ErrorDetail": {
    "message": "user_password_incorrect",
    "en": "Password of user is incorrect",
    "vi": "MαΊ­t khαΊ©u cα»§a Người dΓΉng khΓ΄ng Δ‘ΓΊng"
  },
  "requestId": "0HNC1ERHD53E2:00000001",
  "traceId": "fa7b365b49f1b554a9cfabd978d858c8",
  "spanId": "8623dbe038a6dede"
}
/Domain
  β”œβ”€β”€ /Aggregates/           # Domain aggregates (entities with business rules)
  └── /Common/               # Shared domain logic and base types
       β”œβ”€β”€ AggregateRoot.cs       # Base class for aggregate roots
       β”œβ”€β”€ BaseEntity.cs          # Base class for entities
       └── UlidToStringConverter.cs  # Value converter for ULIDs
/Application
  β”œβ”€β”€ /Common
  β”‚     β”œβ”€β”€ /Auth/                   # custom authorization & policies in .NET Core
  β”‚     β”œβ”€β”€ /Behaviors/              # MediatR pipeline behaviors (CQRS cross‑cutting)
  β”‚     β”œβ”€β”€ /DomainEventHandlers/    # handlers for raising/domain events
  β”‚     β”œβ”€β”€ /Errors/                 # error types for Result‑pattern responses
  β”‚     β”œβ”€β”€ /Exceptions/             # domain/application exception definitions
  β”‚     β”œβ”€β”€ /Extensions/             # helper methods (pagination, LHS parsing, etc.)
  β”‚     β”œβ”€β”€ /Interfaces/             # application‑level contracts & abstractions
  β”‚     β”œβ”€β”€ /QueryStringProcessing/  # validation logic for query‑string params
  β”‚     └── /Security/               # security attributes (e.g. [Authorize], roles)
  β”œβ”€β”€ /Features/                     # CQRS + MediatR pattern modules
  β”‚     β”œβ”€β”€ AuditLogs/               # commands & queries for audit‑trail
  β”‚     β”œβ”€β”€ Common/                  # shared feature utilities
  β”‚     β”œβ”€β”€ Permissions/             # manage app permissions
  β”‚     β”œβ”€β”€ QueueLogs/               # logging for background/queued jobs
  β”‚     β”œβ”€β”€ Regions/                 # region‑related commands & queries
  β”‚     β”œβ”€β”€ Roles/                   # role management (CRUD, assignments)
  β”‚     └── Users/                   # user‑centric commands & queries
  └── DependencyInjection.cs         # Registration of all Application services into DI

/Infrastructure
  β”œβ”€β”€ /Constants/                    # application-wide constants & credential definitions
  β”‚     └── Credential.cs            # strongly-typed credentials (keys, secrets, etc.)
  β”‚
  β”œβ”€β”€ /Data/                         # EF Core data layer: context, migrations, seeding, configs
  β”‚     β”œβ”€β”€ /Configurations/         # IEntityTypeConfiguration<> implementations
  β”‚     β”œβ”€β”€ /Interceptors/           # DbCommand/SaveChanges interceptors (logging, auditing)
  β”‚     β”œβ”€β”€ /Migrations/             # EF Core migration files
  β”‚     β”œβ”€β”€ /Seeds/                  # seed-data providers for initial data
  β”‚     β”œβ”€β”€ DatabaseSettings.cs      # POCO for database connection/settings
  β”‚     β”œβ”€β”€ DbInitializer.cs         # ensures DB is created & seeded on startup
  β”‚     β”œβ”€β”€ DesignTimeDbContextFactory.cs  # design-time factory for `dotnet ef` commands
  β”‚     β”œβ”€β”€ RegionDataSeeding.cs           # specific seed logic for Regions table
  β”‚     β”œβ”€β”€ TheDbContext.cs                # your `DbContext` implementation
  β”‚     └── ValidateDatabaseSetting.cs     # runtime validation of DB settings
  β”‚
  β”œβ”€β”€ /Services/                     # external/infrastructure services & integrations
  β”‚     β”œβ”€β”€ /Aws/                    # AWS SDK wrappers (S3, SNS, etc.)
  β”‚     β”œβ”€β”€ /Cache/                  # caching implementations (Redis, MemoryCache)
  β”‚     β”œβ”€β”€ /ElasticSearch/          # Elasticsearch client & indexing/search logic
  β”‚     β”œβ”€β”€ /Hangfire/               # background-job scheduler configuration
  β”‚     β”œβ”€β”€ /Identity/               # identity provider integrations (JWT, OAuth)
  β”‚     β”œβ”€β”€ /Mail/                   # SMTP, SendGrid, or other mail-sending services
  β”‚     β”œβ”€β”€ /Queue/                  # Request queueing with Redis
  β”‚     β”œβ”€β”€ /Token/                  # token-related services and helpers
  β”‚     β”œβ”€β”€ ActionAccessorService.cs # grabs current `HttpContext` action info
  β”‚     └── CurrentUserService.cs    # resolves authenticated user details
  β”‚
  β”œβ”€β”€ /UnitOfWorks/                  # Unit-of-Work & repository abstractions
  β”‚     β”œβ”€β”€ /CachedRepositories/     # repositories with built-in caching layers
  β”‚     β”œβ”€β”€ /Repositories/           # concrete repository implementations
  β”‚     β”œβ”€β”€ RepositoryExtension.cs   # extension methods for IRepository<T>
  β”‚     └── UnitOfWork.cs            # coordinates multiple repository commits
  β”‚
  └── DependencyInjection.cs         # registration of all Infrastructure services into DI
/Api
  β”œβ”€β”€ /common/                         # shared helpers, configurations for API layer
  β”‚
  β”œβ”€β”€ /Converters/                     # JSON/string converters for date types
  β”‚     β”œβ”€β”€ DateTimeConverter.cs           # custom converter for System.DateTime
  β”‚     └── DateTimeOffsetConverter.cs     # custom converter for System.DateTimeOffset
  β”‚
  β”œβ”€β”€ /Endpoints/                      # minimal‑API endpoint definitions
  β”‚
  β”œβ”€β”€ /Extensions/                     # extension methods (IServiceCollection, HttpContext, etc.)
  β”‚
  β”œβ”€β”€ /Middlewares/                    # custom middleware (error handling, logging, auth, etc.)
  β”‚
  β”œβ”€β”€ /Resources/                      # static resource files
  β”‚     └── /Translations/               # localization .resx files
  β”‚           β”œβ”€β”€ Message.en.resx           # English resource strings
  β”‚           └── Message.vi.resx           # Vietnamese resource strings
  β”‚
  β”œβ”€β”€ /Settings/                       # POCOs bound to appsettings.json sections
  β”‚     β”œβ”€β”€ OpenApiSettings.cs             # swagger/OpenAPI configuration
  β”‚     β”œβ”€β”€ OpenTelemetrySettings.cs       # OTEL exporter/tracing settings
  β”‚     └── SerilogSettings.cs             # Serilog sink & logging configuration
  β”‚
  └── /wwwroot/                        # publicly served static content
        └── /Templates/                   # email/html templates, static assets
            +-----------------------------------------------+
            |                   Api                         |
            +-----------------------------------------------+
             |                     |                    |
             |                     |                    |
             ↓                     |                    |
        +------------------+       |                    |
        |  Infrastructure  |       |                    |
        +------------------+       |                    |
                        |          |                    |
                        ↓          ↓                    ↓
                    +--------------------+    +---------------------+
                    |   Application      | -> | Contracts           |
                    +--------------------+    +---------------------+
                             |
                             ↓
            +---------------------------+
            |          Domain           |
            +---------------------------+

8.1. Run .NET Core Clean Architecture Project

The following prerequisites are required to build and run the solution:

The first step ☝️ :

Create a appsettings.Development.json file at root of Api layer and just copy the content of appsettings.example.json to the file then Modify configurations in your case.

Modify PostgreSQL connection string (this template is using PostgreSQL currently).

"DatabaseSettings": {
    "DatabaseConnection": "Host=localhost;Username=[your_username];Password=[your_password];Database=example"
},

Update migrations to your own database.

cd src/Infrastructure

dotnet ef database update

The next step πŸ‘‰:

change mino username and password at .env if needed and you're gonna use it for logging in Web UI Manager

MINIO_ROOT_USER=the_template_storage
MINIO_ROOT_PASSWORD=storage@the_template1

To Run Amazon S3 service for media file storage.

Access Minio S3 Web UI at http://localhost:9001 and login

Create a pairs of key like

input the keys at your appsettings.json

"S3AwsSettings": {
      "ServiceUrl": "http://localhost:9000",
      "AccessKey": "***",
      "SecretKey": "***",
      "BucketName": "the-template-project",
      "PublicUrl": "http://localhost:9000",
      "PreSignedUrlExpirationInMinutes": 1440,
      "Protocol": 1
    },

The final step

http://localhost:8080/docs is swagger UI path

The default admin account username:chloe.kim, password: Admin@123

Congrats! you are all set up πŸŽ‰ πŸŽ‰ πŸŽ‰ πŸ‘

To Achieve this, let's add RequireAuth on minimal api, permissions parameter is string and Each permission is separated by comma like "create:user,update:user".

app.MapPost(Router.UserRoute.Users, HandleAsync)
    .WithOpenApi(operation => new OpenApiOperation(operation)
    {
        Summary = "Create user πŸ§‘",
        Description = "Creates a new user and returns the created user details.",
        Tags = [new OpenApiTag() { Name = Router.UserRoute.Tags }],
    })
    .WithRequestValidation<CreateUserCommand>()
    .RequireAuth(
        permissions: Permission.Generate(PermissionAction.Create, PermissionResource.User)
    )
    .DisableAntiforgery();
8.2.2. Create role with permissions:

Json payload is like

{
  "description": "this is super admin role",
  "name": "superAdmin",
  "roleClaims": [
    {
      "claimType": "permission",
      "claimValue": "create:customer"
    },
    {
      "claimType": "permission",
      "claimValue": "update:customer"
    }
  ]
}
8.2.3. How to add new permissions in your app

To get this, let's navigate to constants folder in Infrastructure layer, then open Credential.cs file and pay your attention on permissions list

public static readonly List<Dictionary<string, List<string>>> permissions =
    [
        Permission.CreatebasicPermissions(PermissionResource.User),
        Permission.CreatebasicPermissions(PermissionResource.Role),
    ];

Notice that, the key is primary permission and value is list of relative permissions

Permission combines from action and entity name. For example:

Let's take a look at PermissionAction and PermissionResource class

public class PermissionAction
{
    public const string Create = nameof(Create);
    public const string Update = nameof(Update);
    public const string Delete = nameof(Delete);
    public const string Detail = nameof(Detail);
    public const string List = nameof(List);
    public const string Test = nameof(Test);
    public const string Testing = nameof(Testing);
}

public class PermissionResource
{
    public const string User = nameof(User);
    public const string Role = nameof(Role);
}

Define your new one at permissions list then stop and start application again

To do filter in this template, we use LHS Brackets.

LHS is the way to encode operators is the use of square brackets [] on the key name.

For example

GET api/v1/users?filter[dayOfBirth][$gt]="1990-10-01"

This example indicates filtering out users whose birthdays are after 1990/10/01

All support operations:

Operator Description $eq Equal $eqi Equal (case-insensitive) $ne Not equal $nei Not equal (case-insensitive) $in Included in an array $notin Not included in an array $lt Less than $lte Less than or equal to $gt Greater than $gte Greater than or equal to $between Is between $notcontains Does not contain $notcontainsi Does not contain (case-insensitive) $contains Contains $containsi Contains (case-insensitive) $startswith Starts with $endswith Ends with

Some Examples:

GET /api/v1/users?filter[gender][$in][0]=1&filter[gender][$in][1]=2
GET /api/v1/users?filter[gender][$between][0]=1&filter[gender][$between][1]=2
GET /api/v1/users?filter[firstName][$contains]=abc

$and and $or operator:

GET /api/v1/users/filter[$and][0][firstName][$containsi]="sa"&filter[$and][1][lastName][$eq]="Tran"
{
  "filter": {
    "$and": {
      "firstName": "sa",
      "lastName": "Tran"
    }
  }
}
GET /api/v1/users/filter[$or][0][$and][0][claims][claimValue][$eq]=admin&filter[$or][1][lastName][$eq]=Tran
{
    "filter": {
        "$or": {
            "$and":{
                "claims": {
                    "claimValue": "admin"
                }
            },
            "lastName": "Tran"
        }
    }
}

For more examples and get better understand, you can visit

https://docs.strapi.io/dev-docs/api/rest/filters-locale-publication#filtering
https://docs.strapi.io/dev-docs/api/rest/filters-locale-publication#complex-filtering
https://docs.strapi.io/dev-docs/api/rest/filters-locale-publication#deep-filtering

'Cause I designed filter input based on Strapi filter

To Apply dynamic filter, you just call any list method at

unitOfWork.DynamicReadOnlyRepository<User>()

This template supports offset pagination and cursor pagination.

To Enable offset pagination just add this line

var response = await unitOfWork
    .DynamicReadOnlyRepository<User>(true)
    .PagedListAsync(
        new ListUserSpecification(),
        query,
        ListUserMapping.Selector(),
        cancellationToken: cancellationToken
    );

To Enable cursor pagination just add this line

var response = await unitOfWork
    .DynamicReadOnlyRepository<User>(true)
    .CursorPagedListAsync(
        new ListUserSpecification(),
        query,
        ListUserMapping.Selector(),
        cancellationToken: cancellationToken
    );
{
  "results": {
    "data": [
      {
        "firstName": "sang",
        "lastName": "minh",
        "username": "sang.minh123",
        "email": "sang.minh123@gmail.com",
        "phoneNumber": "0925123320",
        "dayOfBirth": "1990-01-09T17:00:00Z",
        "gender": 2,
        "address": "abcdef,XΓ£ PhΖ°α»›c VΔ©nh An,Huyện Cα»§ Chi,ThΓ nh phα»‘ Hα»“ ChΓ­ Minh",
        "avatar": null,
        "status": 1,
        "createdBy": "01JD936AXSDNMQ713P5XMVRQDV",
        "updatedBy": "01JD936AXSDNMQ713P5XMVRQDV",
        "updatedAt": "2025-04-16T14:26:01Z",
        "id": "01JRZFDA1F7ZV4P7CFS5WSHW8A",
        "createdAt": "2025-04-16T14:17:54Z"
      }
    ],
    "paging": {
      "pageSize": 1,
      "totalPage": 3,
      "hasNextPage": true,
      "hasPreviousPage": false,
      "before": null,
      "after": "q+blUlBQci5KTSxJTXEsUbJSUDIyMDLVNTDRNTQLMTK0MjS3MjXRMzG3tDAx1DYwtzIwUNIB6/FMASk2MPQKinJzcTR0M48KMwkwd3YLNg0P9gi3cFTi5aoFAA=="
    }
  },
  "status": 200,
  "message": "Success"
}

If you are having problems, please let me know at issue section.

This project is licensed with the MIT license.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4