DNTFrameworkCore vs ABP Framework

Salar Rabbal
6 min readMar 5, 2019

--

In this post, I want to compare “DNTFrameworkCore” with “ABP Framework”.

ABP is one of most popular and well documented frameworks with high level abstraction that I learned a lot from it. It has many other features that I don’t list them in this post, because, DNTFrameworkCore has not them actually. Behind of ABP, there is a team. It has 123 contributors in GitHub. Also, all of features have related unit-tests.

In other side, DNTFrameworkCore is lightweight with low level abstraction. It is personal open-source project without any contributor and has unit-tests for small part.

Application Services

From the perspective of “Application Services”, in DNTFrameworkCore we can see the following interface and it’s implementation based-on EntityFrameworkCore without any abstraction over it (abstraction over abstraction).

public interface ICrudService<in TKey, TReadModel, TModel, in TFilteredPagedQueryModel> : IApplicationService 
where TModel : MasterModel<TKey>
where TReadModel : MasterModel<TKey>
where TFilteredPagedQueryModel : IFilteredPagedQueryModel
where TKey : IEquatable<TKey>
{
Task<IPagedQueryResult<TReadModel>> ReadPagedListAsync(TFilteredPagedQueryModel model);
Task<Maybe<TModel>> FindAsync(TKey id);
Task<IReadOnlyList<TModel>> FindAsync(IEnumerable<TKey> ids);
Task<IReadOnlyList<TModel>> FindAsync(params TKey[] ids);
Task<IReadOnlyList<TModel>> FindAsync();
Task<IPagedQueryResult<TModel>> FindPagedListAsync(PagedQueryModel model);
Task<bool> ExistsAsync(TKey id);
Task<Result> CreateAsync(TModel model);
Task<Result> CreateAsync(IEnumerable<TModel> models);
Task<Result> EditAsync(TModel model);
Task<Result> EditAsync(IEnumerable<TModel> models);
Task<Result> DeleteAsync(TModel model);
Task<Result> DeleteAsync(IEnumerable<TModel> models);
Task<Result> DeleteAsync(TKey id);
Task<Result> DeleteAsync(IEnumerable<TKey> ids);
}

In this design we have some contract such as:

  • Behind of each CrudService, there is an AggregateRoot entity, it means, CrudService per AggregateRoot.
  • One DTO/Model per AggregateRoot for create and update operations.
  • Based on functional programming concepts, for handling failure scenarios, we use Result class instead of throwing custom exception and use it to application flow control.
  • CUD related methods accept collection of DTO/Model. In many domains, this feature is valuable from the perspective of performance.
  • FindAsync methods are used for fetching an AggregateRoot to doing CUD operation on that.
  • ReadPagedListAsync method is default Read method that accept IFilteredPagedQueryModel for paging, dynamic filtering and sorting.
  • Based on Master-Detail or Aggregate update capability as another valuable feature, all of related entities to an AggregateRoot should be added, deleted and updated with own master and there is not any business/service for child entities. It means there is not separate transaction for child entities.
  • Based on functional programming concepts, Maybe<T> structure should be used, when result of method can be null.
  • Based on CQS principle, every method should either be a command that performs an action, or a query that returns data to the caller, but not both but they can . in this design, CUD operation related methods just are commands and just return failure/success result that no conflict with CQS at all and increase readability.

In other side, in ABP Framework, there is the following interface and it’s implementation that use Implemented Repository as abstraction over ORMs or other DAL technologies.

public interface IAsyncCrudAppService<TEntityDto, TPrimaryKey, in TGetAllInput, in TCreateInput, in TUpdateInput, in TGetInput, in TDeleteInput> 
: IApplicationService
where TEntityDto : IEntityDto<TPrimaryKey>
where TUpdateInput : IEntityDto<TPrimaryKey>
where TGetInput : IEntityDto<TPrimaryKey>
where TDeleteInput : IEntityDto<TPrimaryKey>
{
Task<TEntityDto> Get(TGetInput input);
Task<PagedResultDto<TEntityDto>> GetAll(TGetAllInput input);
Task<TEntityDto> Create(TCreateInput input);
Task<TEntityDto> Update(TUpdateInput input);
Task Delete(TDeleteInput input);
}

In this design, there are the following considerations:

DTO/Model

From the perspective of base models, there are following classes in DNTFrameworkCore:

Note: DetailModel and MasterModel are used for Master-Detail scenarios that I explained it.

In other side, in ABP there are the following classes:

Authorization

There is not authorization interception on Application Services in DNTFrameworkCore. I Implement and use Custom ASP.NET Core AuthorizationProvider to apply dynamic permission-based authorization that I wrote about it in separate post.

In other side, ABP use authorization interception on Application Services as a cross-cutting concern.

Also, DNTFrameworkCore use ABP’s AthorizationProvider mechanism to gather permissions.

BackgourndJobs

There is not any infrastructure or abstraction over existing libraries to manage background scheduled jobs in DNTFrameworkCore and I suggest you that use ASP.NET Core 2.2 IHostedService to this purpose or any other libraries like DNTScheduler.Core as lightweight library.

In other side, ABP has strong Infrastructure and abstraction to manage BackgroundJobs.

Also, DNTFrameworkCore has an infrastructure for manage long-running tasks based on built-in ASP.NET Core IHostedService.

Configuration

DNTFrameworkCore use Microsoft.Extensions.Configuration library to manage startup configurations, while ABP has own infrastructure.

ABP has strong infrastructure to manage Name-Value settings. In other side, DNTFrameworkCore has not implementation for this purpose for now.

Dependency Management

DNTFrameworkCore use Microsoft.Extensions.DependencyInjection library as built-in .NET Core IoC Container, while ABP use Castle.Windsor as a Third party IoC Container near own infrastructure to manage DI.

Eventing

DNTFrameworkCore has a lightweight EventBus implementation to provide Event-Driven mechanism to expose some extensibility points from your “Application Services” and are triggered inside CUD methods as Collection-based BusinessEvent, while ABP follow Domain Events pattern and also it has EntityChangeEvent mechanism.

Transaction/Connection/UnitOfWork

DNTFrameworkCore follow “DbContext Per Request” pattern and an ambient transaction using by ITransactionProvider and an Interceptor. Also use EntityFrameworkCore directly without any abstraction on that, while ABP has own approach and strong infrastructure to provide capability to achieve “Persistence Ignorance” principle to able replace DAL technology.

Based on DNTFrameworkCore approach, if you need to use another ORM/Micro ORM, you should implement one CrudService and use all of features of them.

Localization

DNTFrameworkCore use Microsoft.Extensions.Localization library as built-in ASP.NET Core localization package, While ABP has own infrastructure for this purpose.

Logging

DNTFrameworkCore use Microsoft.Extensions.Logging library as built-in ASP.NET Core logging package, While ABP has own infrastructure for this purpose based-on Castle Logging.

Also DNTFrameworkCore provide “Database Logging” provider that implemented as an adapter for Microsoft.Extensions.Logging library based-on EntityFrameworkCore.

MultiTenancy

From the perspective of Multitenancy, DNTFrameworkCore has different implementation from APB. DNTFrameworkCore has Hooks mechanism and populate TenantId of ITenantEntity and in other side use EntityFrameworkCore’s “Global Query Filtering” mechanism to apply RLS based on TenantId.

Note: small part of automatic-apply query filter is taken from ABP.

Auditing

This mechanism completely is taken from ABP.

Caching

DNTFrameworkCore use Microsoft.Extentions.Caching as a built-in ASP.NET Core caching library, while ABP has own infrastructure.

Validation

DNTFrameworkCore like ABP check validation rules on Application Services as application entry point and this section taken from ABP but with some modification to apply functional programming error handling approach that I explained it.

ASP.NET Core Web API CrudController

This generalization related to DNTFrameworkCore specially. You can easily expose CRUD API for an AggregateRoot like the following example:

In other side, ABP provide robust “Dynamic Web API Layer”.

Numbering

This infrastructure related to DNTFrameworkCore specially to generate unique number for INumberedEntity that I published a post about that.

In other side, ABP has not this mechanism.

Conclusion

I wrote this post to prove that thinking behind of DNTFrameworkCore completely different from ABP (at least in most sections) and I’ll send own PR requests to ABP team to merge some features like Numbering, Master-Detail mechanism and functional programming error handling mechanism, with ABP.

I’m reviewing ABP source code and other open-source project every week. I believe this is one of best ways to learn software design. As I said, DNTFrameworkCore is lightweight and extensible with low depdency for other thrid-party libraries and it focused on CURD-based Application Layer. Also, after DNTFrameworkCore.Cqrs released, you can implement CQRS-based Application Layer with this infrastructure.

DNTFrameworkCore: Lightweight and Extensible Infrastructure for Building Web Applications

ABP: ASP.NET Boilerplate is a general purpose application framework specially designed for new modern web applications. It uses already familiar tools and implements best practices around them to provide you a SOLID development experience. ASP.NET Boilerplate works with the latest ASP.NET Core & EF Core but also supports ASP.NET MVC 5.x & EF 6.x as well.

--

--