WMSSeptember 2026 · 4 min read

WMS Vertical Slice Architecture

By go2ismail · Published · .NET 10

At a glance

The Warehouse feature keeps components, service calls, endpoints, and CQRS code near one another. Shared database, authentication, and response behavior still connect the slices.

Use one feature to understand the architecture

WMS Vertical Slice Architecture organizes code around a business feature so that a developer can follow a use case without beginning in several separate layer projects. In Indotalent’s Blazor WMS Source Code, Warehouse is a useful first slice: it includes components, a service, endpoint mappings, request and response types, handlers, and validators in one feature area.

This article traces the create operation. It describes the actual single-project structure rather than presenting a generic architecture diagram and assuming that the product follows it. The project still has shared infrastructure, and inventory operations can depend on other business data.

Locate the feature boundary

Features/Inventory/Warehouse/
  Components/
    WarehousePage.razor
    _WarehouseCreateForm.razor
    _WarehouseDataTable.razor
    _WarehouseUpdateForm.razor
  Cqrs/
    CreateWarehouseHandler.cs
    CreateWarehouseValidator.cs
    GetWarehouseListHandler.cs
    ...
  WarehouseService.cs
  WarehouseEndpoint.cs

The folder groups related operations around Warehouse rather than giving every individual command a completely independent project. CreateWarehouseHandler.cs contains its request DTO, response DTO, command record, and handler. Reading that file provides the local contract and implementation together.

Step 1: collect input in the Blazor component

_WarehouseCreateForm.razor binds a CreateWarehouseRequest to MudBlazor fields. It validates the form, manages a processing state, and invokes WarehouseService.CreateWarehouseAsync. Success produces a notification and a callback. The component owns these interface concerns; it does not contain the EF entity creation logic.

Step 2: cross the service and endpoint boundary

WarehouseService creates a RestRequest for api/warehouse, adds the DTO as JSON, and executes it through BaseService. That shared code handles available bearer-token headers and response processing. WarehouseEndpoint maps the POST action and sends a CreateWarehouseCommand through IMediator.

// Actual command contract
public record CreateWarehouseCommand(CreateWarehouseRequest Data)
    : IRequest<CreateWarehouseResponse>;

The complete route includes the /api group mapped in Program.cs. This is an HTTP boundary even though the UI and API are part of the same application project. The warehouse API guide explains methods, response status, and direct-client behavior.

Step 3: perform the use case in the handler

CreateWarehouseHandler receives AppDbContext through its constructor. It checks whether a warehouse already has the requested name with AnyAsync. If so, it throws AlreadyExistsException. Otherwise it creates a Warehouse with Name, Description, and SystemWarehouse, adds the entity, and awaits SaveChangesAsync before returning Id and Name.

That handler is a clear place to locate a business change such as a new warehouse attribute. The change may still require editing the request, response, component, entity, and schema. Proximity helps navigation; it does not eliminate the need to keep those contracts aligned.

Step 4: account for shared behavior

AppDbContext supplies audit and soft-delete handling, plus asynchronous automatic numbering for participating entity types. The response helper wraps the returned DTO. Authentication is configured outside the handler. These shared paths affect more than Warehouse, so changing them warrants checks across their consumers.

Inventory calculation is another shared responsibility. Movement features call InventoryTransactionHelper to derive direction and signed stock. A new movement type may require work both in its feature and in that helper. The architecture should therefore be described as feature-oriented organization with shared infrastructure, not as proof of zero coupling.

Place validation at an enforced boundary

CreateWarehouseValidator validates CreateWarehouseRequest, while the dispatched MediatR message is CreateWarehouseCommand. The generic validation behavior looks for validators of the dispatched type. The form explicitly uses the DTO validator; automatic command-pipeline validation of that payload is not established by the DTO validator alone.

If you extend server validation, make the relationship explicit with a command validator or an explicit DTO-validation call. Test the API without the Blazor form as well. This example shows why tracing the complete slice is more useful than assuming that a folder named Cqrs guarantees every cross-cutting behavior.

Extend a slice without losing the operational result

For a warehouse field, identify the UI input and reload behavior, update the DTO mappings, and plan persistence changes. For a movement feature, additionally define document status, transaction creation, signed quantity, and report inclusion. Reuse the existing structure where it fits, while keeping the new business rule visible in the relevant handler or shared calculation code.

Keep verification tied to outcomes: successful creation and reload, duplicate-name handling, invalid direct API input, and deletion of a system warehouse. For stock changes, reconcile the resulting confirmed balance. These scenarios cover the public behavior of the slice more effectively than checking only that a new folder exists.

Move from architecture study to a working codebase

The Blazor UI guide examines the screen and service in more detail, and the EF Core guide explains the persistence path. All of these views lead to the same Blazor WMS source-code product, where you can review the complete application available for customization.