{"id":28170985,"url":"https://github.com/rodrigopaml/tasksapi","last_synced_at":"2025-05-15T18:15:46.702Z","repository":{"id":284260311,"uuid":"954353173","full_name":"RodrigoPAml/TasksAPI","owner":"RodrigoPAml","description":"Base template project for development with ASP.NET server using DDD, CQRS and Clean Architecture principles.","archived":false,"fork":false,"pushed_at":"2025-04-23T13:17:57.000Z","size":110,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-23T13:41:26.488Z","etag":null,"topics":["back-end","clean-architecture","ddd"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RodrigoPAml.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-03-25T00:33:10.000Z","updated_at":"2025-04-23T13:18:01.000Z","dependencies_parsed_at":null,"dependency_job_id":"e1b3ba38-8b3a-4374-9cef-5535608f003d","html_url":"https://github.com/RodrigoPAml/TasksAPI","commit_stats":null,"previous_names":["rodrigopaml/tasksapi"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RodrigoPAml%2FTasksAPI","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RodrigoPAml%2FTasksAPI/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RodrigoPAml%2FTasksAPI/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RodrigoPAml%2FTasksAPI/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RodrigoPAml","download_url":"https://codeload.github.com/RodrigoPAml/TasksAPI/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254394687,"owners_count":22063984,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["back-end","clean-architecture","ddd"],"created_at":"2025-05-15T18:15:43.723Z","updated_at":"2025-05-15T18:15:46.682Z","avatar_url":"https://github.com/RodrigoPAml.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TasksAPI\n\nBase template project for development with ASP.NET server using DDD, CQRS and Clean Architecture principles.\n\nThe project contains some base entities to simulate tasks, categories and users.\n\nYou may need configure *appsettings.json* to get it to work.\n\nThis is a sample, you may change it and adapt to your needs.\n\n## Features\n\n* Entity framework ORM (tested with SQL Server)\n* DDD based implementation\n* Unit and Integration Tests\n* CQRS pattern for use cases\n* Cross-cutting concerns: Auto logging with independent transaction, error handler, auto rollback\n* Migrations system\n* Result pattern return, avoiding throws \n* Api fixed base response\n* Unity of Work and repository pattern\n* API Base result format\n* JWT Authentication\n* BCrypt for security of passwords\n\n# Layers\n\nThe project has a layered structure as follows:\n\n![image](https://github.com/user-attachments/assets/64e72fce-3414-4816-8668-91877f938ba4)\n\nNote: persistence and infraestructure are the same in my project\n\n## API (Presentation)\n\nThis layer is responsible for the presentation of the application, defining endpoints (controllers) and responses.\n\nThis layer typically receives data from the front-end and calls a mediator through the CQRS pattern, which then invokes the application layer.\n\nIt should also handle how the response data should be presented.\n\n```C#\n/// \u003csummary\u003e\n/// Create a new category\n/// \u003c/summary\u003e\n[Authorize]\n[HttpPost]\n[SwaggerResponse(StatusCodes.Status201Created, Type = typeof(BaseResponse))]\n[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(BaseResponse))]\n[SwaggerResponse(StatusCodes.Status500InternalServerError, Type = typeof(BaseResponse))]\npublic async Task\u003cIActionResult\u003e CreateCategory([FromBody] CreateCategoryCommand command)\n{\n    return await HandleApplicationResponse\u003cOperation\u003e(\n          command,\n          (resp) =\u003e\n          {\n              return new()\n              {\n                  Success = resp.Success,\n                  Response = null,\n                  ErrorMessage = resp.Message,\n                  Code = resp.Success ? 201 : 400\n              };\n          }\n    );\n}\n```\n\n## Application (Use cases)\n\nThis layer coordinates the use cases, interacting with the domain layer, infrastructure layer, and presentation layer. \n\nIn our project, the CQRS pattern is used. In the code, you can see that the application layer is responsible for invoking the object creation, validating it, persisting it, and returning the result.\n\n```C#\npublic async Task\u003cOperation\u003e Handle(CreateCategoryCommand request, CancellationToken cancellationToken)\n{\n    if (request == null)\n        return Operation.MakeFailure(\"Invalid request\");\n\n    var createModel = _mapper.Map\u003cCreateCategoryModel\u003e(request);\n    createModel.UserId = _tokenService.GetToken().Id;\n\n    var newCategoryResult = await _categoryBusiness.Create(createModel);\n\n    if(!newCategoryResult.Success)\n        return Operation.MakeFailure(newCategoryResult.Message);  \n\n    await _uow.Begin();\n\n    await _categoryRepo.Create(newCategoryResult.Content);\n\n    await _uow.Save();\n    await _uow.Commit();\n\n    return Operation.MakeSuccess();\n}\n```\n\n## Domain (Core)\n\nThis layer contains the business logic and is independent of all other layers. Here, we have the domain entities modeled with the business logic.\n\n```C#\n/// \u003csummary\u003e\n/// Category entity\n/// \u003c/summary\u003e\npublic sealed class Category : BaseEntity\n{\n    public string Name { get; private set; }\n    public int UserId { get; private set; }\n\n    private Category() { }\n\n    private Category(string name, int userId)\n    {\n        Name = name;\n        UserId = userId;\n    }\n\n    public static Result\u003cCategory\u003e Create(string name, int userId)\n    {\n        var result = ValidateAll(name, userId);\n\n        if (!result.Success)\n            return Result.MakeFailure\u003cCategory\u003e(result.Message);\n\n        var category = new Category(name, userId);\n\n        return Result.MakeSuccess(category);\n    }\n...\n```\n\n## Infraestructure\n\nThis layer is responsable for data persistence and other services, usually will contain the code for the ORM and return domain entities.\n\nIn this layer we also have the persistence entities that are used by the ORM.\n\n```C#\n  /// \u003csummary\u003e\n  /// Repository implementation for the Category entity\n  /// \u003c/summary\u003e\n  public class CategoryRepository : Repository\u003cDbCategory\u003e, ICategoryRepository\n  {\n      public CategoryRepository(DatabaseContext ctx, IServiceProvider provider) : base(ctx.Categories, provider)\n      {\n      }\n\n      public async Task\u003cbool\u003e ExistsByName(string name, int? userId, int? currentId)\n      {\n          var filter = new Filter\u003cDbCategory\u003e(x =\u003e x.Name == name);\n\n          if(userId.HasValue \u0026\u0026 userId != default)\n              filter.And(x =\u003e x.UserId == userId.Value);\n\n          if(currentId.HasValue \u0026\u0026 currentId != default)\n              filter.And(x =\u003e x.Id != currentId.Value);\n          \n          return await _dbSet.AnyAsync(filter.GetExpression());\n      }\n...\n```\n\n## IoC (Inversion of Control)\n\nResponsible for the dependency injection (DI) and resolving dependencies of services.\n\n# Swagger\n\nSwagger is configured with basic documentation. It's possible to see the input data and the returning data according to the response code\n\n![image](https://github.com/user-attachments/assets/3e375be0-16c4-41bc-aebc-df7e09d5c0e0)\n\n# Tests\n\nThe project contains two test projects: unit tests and integration tests.\n\n## Unit tests\n\nIn our project, unit tests were performed in the domain layer, at the entity and domain service levels, using Moq to ensure there were no external dependencies (from repositories).\n\nYou may need configure *appsettings.Tests.json* to get it to work.\n\n### Entities\n![image](https://github.com/user-attachments/assets/0750163d-ceea-4ffa-8f0a-e45068f44737)\n### Domain services\n![image](https://github.com/user-attachments/assets/82a2db13-135f-4e17-b27f-afa4a1e6c37e)\n\n## Integration tests\n\nIn our project, integration tests are performed using a test SQL Server with fixed data at the infrastructure, application, and API layers.\n\n### Infrastructure\n![image](https://github.com/user-attachments/assets/6b301d58-06d8-4dc4-b48e-c868f35b28ae)\n### Application\n![image](https://github.com/user-attachments/assets/85905bed-1c4a-42bc-ac3e-14336ad77269)\n### Api\n![image](https://github.com/user-attachments/assets/757237af-f4ae-42d5-9436-a0209b4c5e38)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frodrigopaml%2Ftasksapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frodrigopaml%2Ftasksapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frodrigopaml%2Ftasksapi/lists"}