Skip to content

Application Develop

project-spec -> architecture-design -> domain-develop -> application-develop -> adapter-develop -> observability-develop -> test-develop

  • Reads domain/03-implementation-results.md generated by the domain-develop skill to confirm the domain model (Aggregate, VO, Event, Specification).
  • If prerequisite documents are missing, the user is asked directly.

When implementing a CQRS-based Application layer, Command/Query/EventHandler use cases repeat the same nested class structure (Request, Response, Validator, Usecase). The Apply merge pattern for Value Object validation, LINQ-based functional chaining, and guard() condition checks are patterns commonly applied across all use cases.

The /application-develop skill automates this repetition. When you convey use case requirements in natural language, it generates Commands, Queries, EventHandlers, and Validators matching Functorium framework patterns in 4 steps.

StepTaskDeliverable
1Use case decompositionCommand/Query/Event identification, Request/Response design
2Port identificationRequired Port interfaces: Repository, Query Adapter, External API, etc.
3Handler implementationApply pattern, LINQ chaining, guard condition checks applied
4Implementation verificationdotnet build + dotnet test passing
PatternRequest InterfaceHandler InterfaceDescription
CommandICommandRequest<TSuccess>ICommandUsecase<TCommand, TSuccess>State change (write)
QueryIQueryRequest<TSuccess>IQueryUsecase<TQuery, TSuccess>Data retrieval (read)
EventIDomainEventIDomainEventHandler<TEvent>Domain event handling
ValidationAbstractValidator<Request>FluentValidation rules
PatternUsage
LINQ functional chainingfrom x in repo.Method() select new Response(...)
Apply merge(v1, v2, v3).Apply((a, b, c) => ...).As().ToFin()
guard condition checkfrom _ in guard(!exists, ApplicationError.For<T>(...))
Execution flowawait usecase.Run().RunAsync() -> .ToFinResponse()
Application errorApplicationError.For<TUsecase>(new AlreadyExists(), value, message)

There are two ways to merge Value Object validation results into a FinT chain. Choose based on the situation.

CriterionApplyT PatternUnwrap (Manual Branch) Pattern
On VO validation failureAccumulates errors and returns them all at onceReturns immediately at the first failure point
Number of VOsEffective with 2 or moreWhen there’s 1 or sequential dependency
Code form(v1, v2).Apply((a, b) => ...).As().ToFin()if (createData.IsFail) return ...;
Use scenarioCreate-type (multiple field simultaneous validation)Update-type (single field change)

ApplyT Pattern — Parallel validation, error accumulation:

var createData = (ProductName.Validate(request.Name), Money.Validate(request.Price))
.Apply((n, p) => Product.Create(
ProductName.Create(n).ThrowIfFail(),
Money.Create(p).ThrowIfFail()))
.As()
.ToFin();

Unwrap Pattern — Sequential validation, immediate return:

var name = ProductName.Create(request.Name);
if (name.IsFail) return name.Match(Succ: _ => throw new InvalidOperationException(), Fail: FinResponse.Fail<Response>);
/application-develop Create a product creation Command Usecase. Include Name, Price in the Request.

Invoking /application-develop without arguments starts the skill in interactive mode, collecting requirements through conversation.

  1. Use case analysis — Shows Command/Query/Event identification results in a table
  2. User confirmation — Proceed to code generation after confirming the analysis results
  3. Code generation — Generates use cases with the nested class pattern
  4. Build/test verification — Runs dotnet build and dotnet test to confirm passing

Example 1: Beginner — Single Product Creation Command

Section titled “Example 1: Beginner — Single Product Creation Command”

The most basic Command use case. Shows the full structure of the nested class pattern (Request, Response, Validator, Usecase) and how to merge Value Object validation with the Apply pattern.

/application-develop Create a product creation Command Usecase. Include Name, Price in the Request.
DeliverableTypeDescription
CommandCreateProductCommandNested class pattern (Request, Response, Validator, Usecase)
RequestRequest(string Name, decimal Price)ICommandRequest<Response>
ValidatorValidatorFluentValidation + MustSatisfyValidation
UsecaseUsecaseICommandUsecase<Request, Response>, LINQ chaining

Nested class pattern — Request, Response, Validator, Usecase cohesively in a single file:

public sealed class CreateProductCommand
{
public sealed record Request(string Name, decimal Price)
: ICommandRequest<Response>;
public sealed record Response(string ProductId, string Name, decimal Price, DateTime CreatedAt);
public sealed class Validator : AbstractValidator<Request>
{
public Validator()
{
RuleFor(x => x.Name).MustSatisfyValidation(ProductName.Validate);
RuleFor(x => x.Price).MustSatisfyValidation(Money.Validate);
}
}
public sealed class Usecase(IProductRepository productRepository)
: ICommandUsecase<Request, Response>
{
private readonly IProductRepository _productRepository = productRepository;
public async ValueTask<FinResponse<Response>> Handle(Request request, CancellationToken cancellationToken)
{
// 1. Value Object validation (Apply pattern)
var createData = CreateProductData(request);
if (createData.IsFail)
{
return createData.Match(
Succ: _ => throw new InvalidOperationException(),
Fail: error => FinResponse.Fail<Response>(error));
}
var product = (Product)createData;
// 2. LINQ functional chaining
FinT<IO, Response> usecase =
from created in _productRepository.Create(product)
select new Response(
created.Id.ToString(),
created.Name,
created.Price,
created.CreatedAt);
Fin<Response> response = await usecase.Run().RunAsync();
return response.ToFinResponse();
}
private static Fin<Product> CreateProductData(Request request)
{
var name = ProductName.Validate(request.Name);
var price = Money.Validate(request.Price);
return (name, price)
.Apply((n, p) =>
Product.Create(
ProductName.Create(n).ThrowIfFail(),
Money.Create(p).ThrowIfFail()))
.As()
.ToFin();
}
}
}

Example 2: Intermediate — Command + Query + Validator

Section titled “Example 2: Intermediate — Command + Query + Validator”

Adds a Query use case and duplication validation to Example 1. Shows duplication checking with guard() in a Command, retrieval via LINQ chaining in a Query, and the dual validation strategy of FluentValidation and Value Objects.

/application-develop Implement product CRUD. Create/Update Commands, ID lookup/search Queries, name duplication validation included.
DeliverableTypeDescription
CommandCreateProductCommandCreate + name duplication check (guard)
CommandUpdateProductCommandUpdate + Apply merge
QueryGetProductByIdQueryID-based single item retrieval
QuerySearchProductsQueryPagination/sort-supported search
ValidatorEach Command’s ValidatorFluentValidation + MustSatisfyValidation

Duplication check with guard — Declarative condition check within a LINQ chain:

using static Functorium.Applications.Errors.ApplicationErrorType;
FinT<IO, Response> usecase =
from exists in _productRepository.Exists(new ProductNameUniqueSpec(productName))
from _ in guard(!exists, ApplicationError.For<CreateProductCommand>(
new AlreadyExists(),
request.Name,
$"Product name already exists: '{request.Name}'"))
from created in _productRepository.Create(product)
select new Response(created.Id.ToString(), created.Name, created.Price, created.CreatedAt);

Query use case — Retrieval through Port, DTO mapping via LINQ chaining:

public sealed class GetProductByIdQuery
{
public sealed record Request(string ProductId) : IQueryRequest<Response>;
public sealed record Response(string ProductId, string Name, decimal Price, DateTime CreatedAt);
public sealed class Usecase(IProductDetailQuery productDetailQuery)
: IQueryUsecase<Request, Response>
{
private readonly IProductDetailQuery _productDetailQuery = productDetailQuery;
public async ValueTask<FinResponse<Response>> Handle(Request request, CancellationToken cancellationToken)
{
var productId = ProductId.Create(request.ProductId);
FinT<IO, Response> usecase =
from result in _productDetailQuery.GetById(productId)
select new Response(result.ProductId, result.Name, result.Price, result.CreatedAt);
Fin<Response> response = await usecase.Run().RunAsync();
return response.ToFinResponse();
}
}
}

Example 3: Advanced — EventHandler + Domain Service Integration

Section titled “Example 3: Advanced — EventHandler + Domain Service Integration”

Adds domain event handlers and Domain Service to Example 2. Implements credit limit validation via Domain Service during order creation, inventory deduction through order created event handler, and inventory restoration on order cancellation — a cross-Aggregate flow.

/application-develop Implement inventory deduction EventHandler on order creation, Domain Service validation when credit limit exceeded,
and inventory restoration on order cancellation.
DeliverableTypeDescription
CommandCreateOrderWithCreditCheckCommandOrder creation with Domain Service integration
EventHandlerOnOrderCreatedIDomainEventHandler<Order.CreatedEvent>, inventory deduction
EventHandlerOnOrderCancelledIDomainEventHandler<Order.CancelledEvent>, inventory restoration
Domain ServiceOrderCreditCheckServiceCross-Aggregate credit limit validation

Domain Service integration Command — Naturally composing Domain Service in a LINQ chain:

public sealed class Usecase(
ICustomerRepository customerRepository,
IOrderRepository orderRepository,
IProductCatalog productCatalog)
: ICommandUsecase<Request, Response>
{
private readonly OrderCreditCheckService _creditCheckService = new();
public async ValueTask<FinResponse<Response>> Handle(Request request, CancellationToken cancellationToken)
{
// ... Value Object creation, OrderLine construction omitted ...
// Composing Domain Service in LINQ chain
FinT<IO, Response> usecase =
from customer in _customerRepository.GetById(customerId)
from _ in _creditCheckService.ValidateCreditLimit(customer, newOrder.TotalAmount)
from saved in _orderRepository.Create(newOrder)
select new Response(
saved.Id.ToString(),
Seq(saved.OrderLines.Select(l => new OrderLineResponse(...))),
saved.TotalAmount,
saved.CreatedAt);
Fin<Response> response = await usecase.Run().RunAsync();
return response.ToFinResponse();
}
}

Domain event handlerIDomainEventHandler<TEvent> implementation, cross-Aggregate side effects:

public sealed class OnOrderCreated : IDomainEventHandler<Order.CreatedEvent>
{
private readonly IInventoryRepository _inventoryRepository;
public OnOrderCreated(IInventoryRepository inventoryRepository)
{
_inventoryRepository = inventoryRepository;
}
public async ValueTask Handle(Order.CreatedEvent notification, CancellationToken cancellationToken)
{
// Deduct inventory per order line
foreach (var line in notification.OrderLines)
{
var inventory = await _inventoryRepository.GetByProductId(line.ProductId).Run().RunAsync();
if (inventory.IsSucc)
{
var inv = inventory.ThrowIfFail();
inv.Deduct(line.Quantity, DateTime.UtcNow);
await _inventoryRepository.Update(inv).Run().RunAsync();
}
}
}
}