Install
$ agentstack add skill-burakdmir-abp-skills-abp-ddd ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
ABP Framework — Domain Driven Design (DDD)
ABP Framework v10.4 DDD quick reference. Entity, Aggregate Root, Repository, Domain/Application Service, DTO.
Trigger
"ABP entity/aggregate root", "ABP repository", "ABP application service", "ABP DTO", "ABP domain service", "ABP unit of work", "ABP DDD".
Layers
Presentation → Application (Services, DTO, UOW) → Domain (Entity, Aggregate, Domain Service, Repo interface) → Infrastructure (EF Core/MongoDB). DDD primarily concerns Domain + Application.
Entity & Aggregate Root
public class Order : AggregateRoot
{
public string ReferenceNo { get; private set; }
public ICollection Lines { get; private set; }
protected Order() { } // for ORM
public Order(Guid id, string referenceNo) : base(id) // id from outside (IGuidGenerator)
{
ReferenceNo = Check.NotNullOrWhiteSpace(referenceNo, nameof(referenceNo));
Lines = new List();
}
public void AddProduct(Guid productId, int count) // behavior in the entity, private setter
{
if (count ` | CreationTime, CreatorId |
| `AuditedAggregateRoot` | + LastModification* |
| `FullAuditedAggregateRoot` | + IsDeleted/Deletion* (soft-delete) |
### Value Object
```csharp
public class Money : ValueObject
{
public decimal Amount { get; private set; }
public string Currency { get; private set; }
protected override IEnumerable GetAtomicValues() { yield return Amount; yield return Currency; }
}
Domain Service
public class OrderManager : DomainService // *Manager suffix
{
private readonly IOrderRepository _orderRepository;
public OrderManager(IOrderRepository orderRepository) => _orderRepository = orderRepository;
public async Task CreateAsync(string referenceNo)
{
if (await _orderRepository.FindByReferenceAsync(referenceNo) != null)
throw new BusinessException("Orders:ReferenceAlreadyExists");
return new Order(GuidGenerator.Create(), referenceNo); // base class GuidGenerator
}
}
Use: when a rule doesn't fit a single entity / when multiple aggregates are needed. Take/return domain objects, not DTOs; don't depend on the authenticated user.
Domain Events
public void Complete()
{
Status = OrderStatus.Completed;
AddLocalEvent(new OrderCompletedEvent(Id)); // same transaction, synchronous
AddDistributedEvent(new OrderCompletedEto { OrderId = Id }); // asynchronous, ETO
}
public class OrderCompletedHandler : ILocalEventHandler, ITransientDependency
{
public async Task HandleEventAsync(OrderCompletedEvent e) { }
}
// ETO → *.Domain.Shared
[EventName("Orders.OrderCompleted")]
public class OrderCompletedEto { public Guid OrderId { get; set; } }
Application Layer
public class BookDto : AuditedEntityDto { public string Name { get; set; } public float Price { get; set; } }
public class CreateUpdateBookDto
{
[Required, StringLength(128)] public string Name { get; set; }
[Required] public float Price { get; set; }
}
Rule: never expose entities, always DTOs.
Object Mapping (Mapperly — default in v10.4)
[Mapper]
public partial class BookMapper : MapperBase
{
public override partial BookDto Map(Book source);
public override partial void Map(Book source, BookDto destination);
}
// Module: [DependsOn(typeof(AbpMapperlyModule))] + context.Services.AddMapperlyObjectMapper();
var dto = ObjectMapper.Map(book);
> For Mapperly/AutoMapper details see the [Object Mapping skill](../abp-object-mapping/SKILL.md).
Application Service
public class BookAppService : ApplicationService, IBookAppService
{
private readonly IRepository _repo;
public BookAppService(IRepository repo) => _repo = repo;
[Authorize(BookStorePermissions.Books.Create)]
public async Task CreateAsync(CreateUpdateBookDto input)
{
var book = new Book(GuidGenerator.Create(), input.Name, input.Price);
await _repo.InsertAsync(book);
return ObjectMapper.Map(book);
}
}
CrudAppService (reduces boilerplate)
public class BookAppService
: CrudAppService, IBookAppService
{
public BookAppService(IRepository repository) : base(repository)
{
CreatePolicyName = "BookStore.Books.Create"; // GetPolicyName/UpdatePolicyName/DeletePolicyName
}
}
Repository
// Generic (simple CRUD): IRepository → Get/Find/Insert/Update/Delete/GetListAsync/GetPagedListAsync...
// Custom (interface in Domain, impl in EF Core layer)
public interface IBookRepository : IRepository { Task FindByNameAsync(string name); }
public class BookRepository : EfCoreRepository, IBookRepository { /* ... */ }
// Eager loading
var q = await _orderRepository.WithDetailsAsync(x => x.Lines);
var list = await AsyncExecuter.ToListAsync(q);
> Don't expose IQueryable; don't return a projection class. Details: [EF Core](../abp-efcore/SKILL.md).
Unit of Work
UOW is automatic in app service / controller / repository methods. HTTP GET is not transactional.
await _repo.InsertAsync(entity, autoSave: true); // preferred
[UnitOfWork(IsTransactional = false)] public virtual async Task FooAsync() { }
using (var uow = _uowManager.Begin(requiresNew: true)) { /* ... */ await uow.CompleteAsync(); }
Specification
public class ProductsByCategorySpec : Specification
{
private readonly Guid _categoryId;
public ProductsByCategorySpec(Guid id) => _categoryId = id;
public override Expression> ToExpression() => p => p.CategoryId == _categoryId && !p.IsDeleted;
}
var products = await _productRepository.GetListAsync(new ProductsByCategorySpec(categoryId));
Extra Properties
user.SetProperty("Title", "Dr.");
var title = user.GetProperty("Title");
Best Practices
- Aggregate Root + protected setter + constructor validation
- Use DTOs, don't expose entities
- Mapperly
[Mapper]partial class IGuidGenerator.Create()sequential GUID,Clockfor time- Soft-delete →
FullAuditedAggregateRoot - Reduce boilerplate with CrudAppService, rely on UOW conventions
Related
- [EF Core](../abp-efcore/SKILL.md) · [MongoDB](../abp-mongodb/SKILL.md) · [Object Mapping](../abp-object-mapping/SKILL.md) · [Validation](../abp-validation/SKILL.md) · [Dependency Rules](../abp-dependency-rules/SKILL.md) · [Development Flow](../abp-development-flow/SKILL.md)
- ABP Docs: https://abp.io/docs/latest/framework/architecture/domain-driven-design
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: burakdmir
- Source: burakdmir/abp-skills
- License: MIT
- Homepage: https://abp.io/docs/latest
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.