Zeroplat’s backend is built on ASP.NET Core and the ABP.io framework. It follows a modular, Domain-Driven Design (DDD) approach with clear separation of concerns, first-class multi-tenancy, and out-of-the-box building blocks for identity, authorization, settings, auditing, background jobs, caching, and more. This page explains the overall architecture, core projects, and how to extend the backend safely.
Architecture at a Glance
Zeroplat uses the standard ABP layered solution:
Zeroplat.Domain.Shared # Shared constants, enums, permission/setting/feature definitions, localization resources
Zeroplat.Domain # Entities, aggregates, domain services, domain events, repositories (interfaces)
Zeroplat.Application.Contracts# DTOs, service interfaces, validation contracts (no business logic)
Zeroplat.Application # Application services (use cases), mapping, authorization checks
Zeroplat.EntityFrameworkCore # EF Core context, repository implementations, model configurations & migrations
Zeroplat.HttpApi # API controllers (conventional/dynamic), models for transport
Zeroplat.HttpApi.Host # ASP.NET Core host (DI, middleware pipeline, Swagger, config, module bootstrap)
Zeroplat.DbMigrator # Headless app to run database migrations & seed data
Key characteristics:
- Modular: Each layer is a module that declares explicit dependencies.
- DDD friendly: Entities, value objects, domain services, events.
- Unit of Work: Transactional application service execution.
- Multi-Tenancy: Tenant-aware repositories, filters, and setting scopes.
- Environment-aware: Configuration via
appsettings.{Environment}.json
.
Core ABP Modules (Enabled by Design)
- Identity & Permission Management – roles, users, claims, policies, permission checks.
- Tenant Management – first-class multi-tenancy support (tenant filters on data).
- Feature Management – enable/disable features per tenant or globally.
- Setting Management – strongly-typed settings with per-tenant overrides.
- Audit Logging – automatic audit for changes, logins, exceptions.
- Localization – resource-based i18n for errors/UI text.
- Background Jobs – queue and run jobs; optional adapters (e.g., Hangfire).
- Caching – in-memory or Redis via
IDistributedCache
. - Event Bus – local & distributed events for decoupled integrations.
Authentication uses ABP’s authentication stack (OpenIddict/IdentityServer depending on the template), with cookie/JWT flows configurable at the host level.
Data Access
- ORM: Entity Framework Core (default provider: SQL Server; others can be added).
- DbContext: Lives in
Zeroplat.EntityFrameworkCore
. - Migrations: Maintained in EF Core; executed via
Zeroplat.DbMigrator
or EF CLI.
Connection Strings
// appsettings.json
{
"ConnectionStrings": {
"Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=ZeroplatDB;Trusted_Connection=True"
}
}
Per-environment overrides go into
appsettings.Development.json
,appsettings.Staging.json
,appsettings.Production.json
.
HTTP API
- Controllers: Generated conventionally from application services (ABP Dynamic API Controllers) in
Zeroplat.HttpApi
. - Serialization: System.Text.Json (configurable).
- OpenAPI: Swagger UI enabled in
Zeroplat.HttpApi.Host
for discovery & testing.
Multi-Tenancy Model
- Tenant context flows through the request pipeline; repositories apply tenant filters automatically.
- Per-tenant overrides for settings, features, and (optionally) connection strings.
- Storage strategy: single database with tenant discriminator by default; database-per-tenant can be introduced if needed.
Configuration & Environments
- Configuration precedence:
appsettings.json
→appsettings.{Environment}.json
→ environment variables → user secrets (dev). - Secrets: Keep secrets out of source control (User Secrets / Key Vaults).
- Feature Flags & Settings: Centralized in ABP’s providers with scopes (Global / Tenant / User).
Logging, Auditing & Observability
- Logging: ABP integrates with Microsoft.Extensions.Logging (Serilog recommended).
- Auditing: Automatic audit logs for user actions, entity changes, and exceptions.
- Health Checks: Can be enabled for DB, cache, and custom checks.
Background Jobs & Distributed Processing
- Background Jobs: Enqueue fire-and-forget tasks (e.g., email, syncs).
- Event Bus: Use
ILocalEventBus
(in-process) orIDistributedEventBus
(e.g., with RabbitMQ/Kafka) for decoupled workflows. - Caching:
IDistributedCache<T>
with Redis recommended for scale-out.
Typical Request Flow
- HTTP request hits
Zeroplat.HttpApi.Host
(middleware: tenancy, auth, unit of work). - Controller forwards to Application Service (authorization, validation, mapping).
- Domain logic executes (repositories, domain services, events).
- Entity Framework Core persists changes within Unit of Work.
- Response mapped to DTO and returned to the client.
Extending Zeroplat: The Happy Path
1) Add a Domain Entity (Domain Layer)
// Zeroplat.Domain
public class Document : AggregateRoot<Guid>
{
public string Title { get; private set; }
public Guid? TenantId { get; private set; } // tenant-aware
private Document() {} // for ORM
public Document(Guid id, string title, Guid? tenantId = null)
: base(id)
{
SetTitle(title);
TenantId = tenantId;
}
public void SetTitle(string title)
{
Title = Check.NotNullOrWhiteSpace(title, nameof(title), maxLength: 200);
// Raise domain events if needed
}
}
2) Configure EF Core (EF Layer)
// Zeroplat.EntityFrameworkCore
public class DocumentEntityTypeConfiguration : IEntityTypeConfiguration<Document>
{
public void Configure(EntityTypeBuilder<Document> b)
{
b.ToTable("Documents");
b.Property(x => x.Title).IsRequired().HasMaxLength(200);
b.HasIndex(x => x.Title);
}
}
3) Define DTOs & Contracts (Application.Contracts)
public class DocumentDto : EntityDto<Guid>
{
public string Title { get; set; }
}
public class CreateUpdateDocumentDto
{
[Required, MaxLength(200)]
public string Title { get; set; }
}
4) Implement Application Service (Application Layer)
public class DocumentAppService : ApplicationService, IDocumentAppService
{
private readonly IRepository<Document, Guid> _repo;
public DocumentAppService(IRepository<Document, Guid> repo)
{
_repo = repo;
}
[Authorize(ZeroplatPermissions.Documents.Create)]
public async Task<DocumentDto> CreateAsync(CreateUpdateDocumentDto input)
{
var entity = new Document(GuidGenerator.Create(), input.Title, CurrentTenant.Id);
await _repo.InsertAsync(entity, autoSave: true);
return ObjectMapper.Map<Document, DocumentDto>(entity);
}
}
5) Permissions (Domain.Shared)
public static class ZeroplatPermissions
{
public const string GroupName = "Zeroplat";
public static class Documents
{
public const string Default = GroupName + ".Documents";
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
}
}
6) Mapping Profile (Application Layer)
public class ZeroplatApplicationAutoMapperProfile : Profile
{
public ZeroplatApplicationAutoMapperProfile()
{
CreateMap<Document, DocumentDto>();
}
}
7) Migrations & Seeding
- Add a migration in the EF Core project and run it via DbMigrator.
- Seed initial data in the migrator or
DataSeedContributor
.
Validation & Exception Handling
- Validation: DataAnnotations / FluentValidation on DTOs; ABP auto-validates in application services.
- Exceptions: ABP standardizes error responses (problem details), with localization support.
Security & Authorization
- Authorize application service methods with
[Authorize("Permission.Name")]
. - Policies: Compose complex rules (e.g., ownership checks).
- Claims/Roles: Managed via Identity module; permissions assigned to roles.
Settings, Features & Localization
- Settings: Strongly-typed keys, configurable per tenant.
- Features: Toggle functionality per edition/tenant.
- Localization: Resource files in
Domain.Shared
; useL["Key"]
in services.
When to Create a New Module
Create a separate module if the new functionality:
- Has its own domain concepts and dependencies.
- Might be reused/packaged independently.
- Requires separate settings/features/permissions.
Define it as an ABP module with its own *Module
class and declare dependencies explicitly.
Deployment Notes (Backend)
- Configuration: Externalize sensitive values (env vars, vaults).
- Database: Run
Zeroplat.DbMigrator
before starting the host. - Scaling: Use Redis for distributed cache; prefer sticky-less sessions (JWT).
- Logging: Use Serilog sinks (file, Seq, ELK) in production.
Summary
- Zeroplat’s backend is a clean ABP.io architecture with DDD layers, multi-tenancy, and rich infrastructure.
- You build features by adding entities → application services → permissions → mappings → migrations.
- The platform is environment-aware, secure by default, and extensible via modules, events, and settings.
If you want, I can tailor this overview to your exact module list (Identity, SaaS, Audit, etc.) and include a dependency diagram and sidebar TOC entries for your docs.