# BoringStuff **Repository Path**: wzcool/boring-stuff ## Basic Information - **Project Name**: BoringStuff - **Description**: Boring Stuff with ABP - **Primary Language**: C# - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-07-09 - **Last Updated**: 2021-09-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # BoringStuff #### Description Boring Stuff with ABP #### Software Architecture ABP + MySQL + Angular #### Precondition dotnet tool install -g Volo.Abp.Cli dotnet tool update -g Volo.Abp.Cli abp new Boring.Stuff -u angular #### How to initial database Open "package Manager Console", choose "**.EntityFrameworkCore.DbMigrations" as Default project and then run below commands one by one: Add-Migration "Initial" Update-Database #### The Domain Layer 1. Boring.Stuff.Domain It is the essential domain layer that contains all the building blocks (entities, value objects, domain services, specifications, repository interfaces, etc.) introduced before. Database entity will create into this project remember: Aggregates are loaded by their sub-collections as a single unit So, determine your aggregate boundaries and size based on the following considerations; ● Objects used together. ● Query (load/save) performance and memory consumption. ● Data integrity, validity and consistency. In practical; ● Most of the aggregate roots will not have sub-collections. ● A sub-collection should not have more than 100-150 items inside it at the most case. If you think a collection potentially can have more items, don't define the collection as a part of the aggregate and consider to extract another aggregate root for the entity inside the collection. IGuidGenerator The most important problem with GUID is that it is not sequential by default. When you use the GUID as the primary key and set it as the clustered index (which is default) for your table, it brings a significant performance problem on insert (because inserting new record may need to re-order the existing records). So, never use Guid.NewGuid() to create Ids for your entities! One good solution to this problem is to generate sequential GUIDs, which is provided by the ABP Framework out of the box. IGuidGenerator service creates sequential GUIDs (implemented by the SequentialGuidGenerator by default). Use IGuidGenerator.Create() when you need to manually set Id of an entity. public class MyProductService : ITransientDependency { private readonly IRepository _productRepository; private readonly IGuidGenerator _guidGenerator; public MyProductService( IRepository productRepository, IGuidGenerator guidGenerator) { _productRepository = productRepository; _guidGenerator = guidGenerator; } public async Task CreateAsync(string productName) { var product = new Product(_guidGenerator.Create(), productName); await _productRepository.InsertAsync(product); } } 2. Boring.Stuff.Domain.Shared It is a thin project that contains some types those belong to the Domain Layer, but shared with all other layers. For example, it may contain some constants and enums related to the Domain Objects but need to be reused by other layers. #### The Application Layer 1. Boring.Stuff.Application.Contracts It contains the application service interfaces and the DTOs used by these interfaces. This project can be shared by the client applications (including the UI). 2. Boring.Stuff.Application It is the essential application layer that implements the interfaces defined in the Contracts project. #### The Presentation Layer 1. Boring.Stuff.HttpApi.Host or Boring.Stuff.Web This is the only executable application that serves the application and the APIs. Boring.Stuff.Web is an ASP.NET Core MVC / Razor Pages application for this example. ABP Framework also supports different kind of UI frameworks including Angular and Blazor. In these cases, the Boring.Stuff.Web doesn't exist in the solution. Boring.Stuff.HttpApi.Host application will be in the solution to serve the HTTP APIs as a standalone endpoint to be consumed by the UI applications via HTTP API calls. #### The Remote Service Layer 1. Boring.Stuff.HttpApi It contains HTTP APIs defined by the solution. It typically contains MVC Controllers and related models, if available. So, you write your HTTP APIs in this project. FYI: Most of the time, API Controllers are just wrappers around the Application Services to expose them to the remote clients. Since ABP Framework's Automatic API Controller System automatically configures and exposes your Application Services as API Controllers, you typically don't create Controllers in this project. However, the startup solution includes it for the cases you need to manually create API controllers. 2. Boring.Stuff.HttpApi.Client It is useful when you have a C#### application that needs to consume your HTTP APIs. Once the client application references this project, it can directly inject & use the Application Services. This is possible by the help of the ABP Framework's Dynamic C# Client API Proxies System. FYI: There is a Console Application in the test folder of the solution, named IssueTracking.HttpApi.Client.ConsoleTestApp. It simply uses the IssueTracking.HttpApi.Client project to consume the APIs exposed by the application. It is just a demo application and you can safely delete it. You can even delete the IssueTracking.HttpApi.Client project if you think that you don't need to them. #### The Infrastructure Layer In a DDD implementation, you may have a single Infrastructure project to implement all the abstractions and integrations (integrate 3rd party code), or you may have different projects for each dependency. We suggest a balanced approach; Create separate projects for main infrastructure dependencies (like Entity Framework Core) and a common infrastructure project for other infrastructure. ABP's startup solution has two projects for the Entity Framework Core integration; - Boring.Stuff.EntityFrameworkCore is the essential integration package for the EF Core. Your application's DbContext, database mappings, implementations of the repositories and other EF Core related stuff are located here. - Boring.Stuff.EntityFrameworkCore.DbMigrations is a special project to manage the Code First database migrations. There is a separate DbContext in this project to track the migrations. You typically don't touch this project much except you need to create a new database migration or add an application module that has some database tables and naturally requires to create a new database migration. FYI: You may wonder why there are two projects for the EF Core. It is mostly related to modularity. Each module has its own independent DbContext and your application has also one DbContext. DbMigrations project contains a union of the modules to track and apply a single migration path. While most of the time you don't need to know it, you can see the EF Core migrations document for more information. #### Other Projects Boring.Stuff.DbMigrator, that is a simple Console Application that migrates the database schema and seeds the initial data when you execute it. It is a useful utility application that you can use it in development as well as in production environment. #### Dependencies of the Projects in the Solution The projects have been explained before. Now, we can explain the reasons of the dependencies; - Domain.Shared is the project that all other projects directly or indirectly depend on. So, all the types in this project are available to all projects. - Domain only depends on the Domain.Shared because it is already a (shared) part of the domain. For example, an IssueType enum in the Domain.Shared can be used by an Issue entity in the Domain project. - Application.Contracts depends on the Domain.Shared. In this way, you can reuse these types in the DTOs. For example, the same IssueType enum in the Domain.Shared can be used by a CreateIssueDto as a property. - Application depends on the Application.Contracts since it implements the Application Service interfaces and uses the DTOs inside it. It also depends on the Domain since the Application Services are implemented using the Domain Objects defined inside it. - EntityFrameworkCore depends on the Domain since it maps the Domain Objects (entities and value types) to database tables (as it is an ORM) and implements the repository interfaces defined in the Domain. - HttpApi depends on the Application.Contracts since the Controllers inside it inject and use the Application Service interfaces as explained before. - HttpApi.Client depends on the Application.Contracts since it can consume the Application Services as explained before. - Web depends on the HttpApi since it serves the HTTP APIs defined inside it. Also, in this way, it indirectly depends on the Application.Contracts project to consume the Application Services in the Pages/Components.