Amantinband.CleanArchitecture.Template 1.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Amantinband.CleanArchitecture.Template --version 1.0.0                
NuGet\Install-Package Amantinband.CleanArchitecture.Template -Version 1.0.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Amantinband.CleanArchitecture.Template" Version="1.0.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Amantinband.CleanArchitecture.Template --version 1.0.0                
#r "nuget: Amantinband.CleanArchitecture.Template, 1.0.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Amantinband.CleanArchitecture.Template as a Cake Addin
#addin nuget:?package=Amantinband.CleanArchitecture.Template&version=1.0.0

// Install Amantinband.CleanArchitecture.Template as a Cake Tool
#tool nuget:?package=Amantinband.CleanArchitecture.Template&version=1.0.0                

<div align="center">

Build

GitHub contributors GitHub Stars GitHub license codecov


Clean Architecture Template Title


</div>

️Important notice ⚠️

If you like this template, check out my comprehensive course on Dometrain where I cover everything you need to know when building production applications structured following clean architecture.

Give it a star ⭐

Loving it? Show your support by giving this project a star!

Domain Overview 🌍

This is a simple reminder application. It allows users to create and manage their reminders.

To create reminders, a user must have an active subscription.

Basic Subscription

Users with a basic subscription can create up to 3 daily reminders.

Pro Subscription

Users with a pro subscription do not have a daily limit on the number of reminders.

Use Cases / Features πŸ€“

Subscriptions

  1. Create Subscription
  2. Cancel Subscription
  3. Get Subscription

Reminders

  1. Delete Reminder
  2. Dismiss Reminder
  3. Set Reminder
  4. Get Reminder
  5. List Reminders

Getting Started πŸƒ

  1. Run the project dotnet run --project src/CleanArchitecture.Api

  2. Navigate to requests/Tokens/GenerateToken.http and generate a token.

    POST {{host}}/tokens/generate
    Content-Type: application/json
    
    {
        "Id": "bae93bf5-9e3c-47b3-aace-3034653b6bb2",
        "FirstName": "Amichai",
        "LastName": "Mantinband",
        "Email": "amichai@mantinband.com",
        "Permissions": [
            "set:reminder",
            "get:reminder",
            "dismiss:reminder",
            "delete:reminder",
            "create:subscription",
            "delete:subscription",
            "get:subscription"
        ],
        "Roles": [
            "Admin"
        ]
    }
    

    Note: Since most systems use an external identity provider, this project uses a simple token generator endpoint that generates a token based on the details you provide. This is a simple way to generate a token for testing purposes and is closer to how your system will likely be designed when using an external identity provider.

  3. Create a subscription

    POST {{host}}/users/{{userId}}/subscriptions
    Content-Type: application/json
    Authorization: Bearer {{token}}
    
    {
        "SubscriptionType": "Basic"
    }
    

    Note: To replace http file variables {{variableName}}, you can either:

    1. Use the REST Client extension for VS Code + update the values under ".vscode/settings.json". This will update the value for all http files.
    2. Define the variables in the http file itself:
      @host = http://localhost:5001
      @userId = bae93bf5-9e3c-47b3-aace-3034653b6bb2
      
    3. Replace the variables manually.
  4. Create a reminder

    POST {{host}}/users/{{userId}}/subscriptions/{{subscriptionId}}/reminders
    Content-Type: application/json
    Authorization: Bearer {{token}}
    
    {
        "text": "let's do it",
        "dateTime": "2025-2-26"
    }
    

Folder Structure πŸ“

Folder structure

Authorization πŸ”

This project puts an emphasis on complex authorization scenarios and supports role-based, permission-based and policy-based authorization.

Authorization Types

Role-Based Authorization

To apply role based authorization, use the Authorize attribute with the Roles parameter and implement the IAuthorizeableRequest interface.

For example:

[Authorize(Roles = "Admin")]
public record CancelSubscriptionCommand(Guid UserId, Guid SubscriptionId) : IAuthorizeableRequest<ErrorOr<Success>>;

Will only allow users with the Admin role to cancel subscriptions.

Permission-Based Authorization

To apply permission based authorization, use the Authorize attribute with the Permissions parameter and implement the IAuthorizeableRequest interface.

For example:

[Authorize(Permissions = "get:reminder")]
public record GetReminderQuery(Guid UserId, Guid SubscriptionId, Guid ReminderId) : IAuthorizeableRequest<ErrorOr<Reminder>>;

Will only allow users with the get:reminder permission to get a subscription.

Policy-Based Authorization

To apply policy based authorization, use the Authorize attribute with the Policy parameter and implement the IAuthorizeableRequest interface.

For example:

[Authorize(Policies = "SelfOrAdmin")]
public record GetReminderQuery(Guid UserId, Guid SubscriptionId, Guid ReminderId) : IAuthorizeableRequest<ErrorOr<Reminder>>;

Will only allow users who pass the SelfOrAdmin policy to get a subscription.

Each policy is implemented as a simple method in the PolicyEnforcer class.

The policy "SelfOrAdmin" for example, can be implemented as follows:

public class PolicyEnforcer : IPolicyEnforcer
{
    public ErrorOr<Success> Authorize<T>(
        IAuthorizeableRequest<T> request,
        CurrentUser currentUser,
        string policy)
    {
        return policy switch
        {
            "SelfOrAdmin" => SelfOrAdminPolicy(request, currentUser),
            _ => Error.Unexpected(description: "Unknown policy name"),
        };
    }

    private static ErrorOr<Success> SelfOrAdminPolicy<T>(IAuthorizeableRequest<T> request, CurrentUser currentUser) =>
        request.UserId == currentUser.Id || currentUser.Roles.Contains(Role.Admin)
            ? Result.Success
            : Error.Unauthorized(description: "Requesting user failed policy requirement");
}

Mixing Authorization Types

You can mix and match authorization types to create complex authorization scenarios.

For example:

[Authorize(Permissions = "get:reminder,list:reminder", Policies = "SelfOrAdmin", Roles = "ReminderManager")]
public record ListRemindersQuery(Guid UserId, Guid SubscriptionId, Guid ReminderId) : IAuthorizeableRequest<ErrorOr<Reminder>>;

Will only allow users with the get:reminder permission or the list:reminder permission, and who pass the SelfOrAdmin policy, and who have the ReminderManager role to list reminders.

Another option, is specifying the Authorize attribute multiple times:

[Authorize(Permissions = "get:reminder")]
[Authorize(Permissions = "list:reminder")]
[Authorize(Policies = "SelfOrAdmin")]
[Authorize(Roles = "ReminderManager")]
public record ListRemindersQuery(Guid UserId, Guid SubscriptionId, Guid ReminderId) : IAuthorizeableRequest<ErrorOr<Reminder>>;

Testing πŸ“

This project puts an emphasis on testability and comes with a comprehensive test suite.

alternate text is missing from this package README image

Test Types

Domain Layer Unit Tests

The domain layer is tested using unit tests. By the bare minimum, each domain entity should have a test that verifies its invariants.

Domain Layer unit tests

Application Layer Unit Tests

The domain layer is tested using both unit tests and subcutaneous tests.

Since each one of the application layer use cases has its corresponding subcutaneous test, the unit tests are used to test the application layer standalone components, such as the ValidationBehavior and the AuthorizationBehavior.

Application Layer unit tests

Application Layer Subcutaneous Tests

Subcutaneous tests are tests that operate right under the presentation layer. These tests are responsible for testing the core logic of our application, which is the application layer and the domain layer.

The reason there are so many of these tests, is because each one of the application layer use cases has its corresponding subcutaneous test.

This allows us to test the application layer and the domain layer based on the actual expected usage, giving us the confidence that our application works as expected and that the system cannot be manipulated in a way we don't allow.

I recommend spending more effort on these tests than the other tests, since they aren't too expensive to write, and the value they provide is huge.

alternate text is missing from this package README image

Presentation Layer Integration Tests

The api layer is tested using integration tests. This is where we want to cover the entire system, including the database, external dependencies and the presentation layer.

Unlike the subcutaneous tests, the focus of these tests is to ensure the integration between the various components of our system and other systems.

Integration Tests

Contribution 🀲

If you have any questions, comments, or suggestions, please open an issue or create a pull request πŸ™‚

Credits πŸ™

  • CleanArchitecture - An awesome clean architecture solution template by Jason Taylor

License πŸͺͺ

This project is licensed under the terms of the MIT license.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.1.1 3,672 1/14/2024
1.1.0 457 1/5/2024
1.0.5 333 1/1/2024
1.0.4 398 12/26/2023
1.0.3 419 12/19/2023
1.0.2 281 12/17/2023
1.0.1 269 12/16/2023
1.0.0 286 12/14/2023