Skip to content

Where a new feature goes ​

The table ​

WhatWhere
Controller actionInnovayse.Hostpanel.API/Controllers/<Feature>Controller.cs
CommandInnovayse.Hostpanel.Application/<Feature>/Commands/<UseCase>/<UseCase>Command.cs
QueryInnovayse.Hostpanel.Application/<Feature>/Queries/<UseCase>/<UseCase>Query.cs
HandlerInnovayse.Hostpanel.Application/<Feature>/{Commands,Queries}/<UseCase>/<UseCase>Handler.cs
ValidatorInnovayse.Hostpanel.Application/<Feature>/{Commands,Queries}/<UseCase>/<UseCase>Validator.cs
Shared DTOInnovayse.Hostpanel.Application/<Feature>/Common/<Name>Dto.cs
PortInnovayse.Hostpanel.Application/Common/Interfaces/I<Port>.cs
Error codeInnovayse.Hostpanel.Application/Common/Errors/HostpanelErrorCodes.cs
Status mappingShared/Controllers/StatusMapping.cs
Error sentence (en)Innovayse.Hostpanel.Application/Resources/ErrorMessages.resx
Error sentence (ru)Innovayse.Hostpanel.Application/Resources/ErrorMessages.ru.resx
Error sentence (hy)Innovayse.Hostpanel.Application/Resources/ErrorMessages.hy.resx
EntityInnovayse.Hostpanel.Domain/<Feature>/<Entity>.cs
Entity configurationInnovayse.Hostpanel.Infrastructure/Persistence/Configurations/<Entity>Configuration.cs
MigrationInnovayse.Hostpanel.Infrastructure/Persistence/Migrations/
AdapterInnovayse.Hostpanel.Infrastructure/<Capability>/<Adapter>.cs
Integration testInnovayse.Hostpanel.Integration.Tests/<Feature>/<UseCase>Tests.cs
Admin screenadmin/src/views/<area>/<ScreenName>View.vue
Admin storeadmin/src/stores/<area>.ts
Admin API composableadmin/src/composables/apis/use<Area>Api.ts
Admin end-to-end testadmin/e2e/<area>.spec.ts
Client pageclient/pages/<area>/<page>.vue
Client storeclient/stores/<area>.ts
Client API composableclient/composables/use<Area>Api.ts
Portal server routeclient/server/api/portal/<area>/

A worked example ​

This example adds a read-only admin list screen for a new "Labels" feature.

1. Query, handler and validator

Create Innovayse.Hostpanel.Application/Labels/Queries/GetLabels/GetLabelsQuery.cs, GetLabelsHandler.cs and GetLabelsValidator.cs. The handler takes IHostpanelDbContext, reads the rows and returns a Result<IReadOnlyList<LabelDto>>. The DTO LabelDto lives in Labels/Common/LabelDto.cs; there are no *Response wrapper types.

2. Controller action

Add a GET /api/admin/labels action to Innovayse.Hostpanel.API/Controllers/LabelsController.cs (create it if it does not exist). The action creates a GetLabelsQuery, calls await _bus.InvokeAsync<Result<IReadOnlyList<LabelDto>>>(query, ct) and passes the result to HandleResult.

3. Integration test

Create Innovayse.Hostpanel.Integration.Tests/Labels/GetLabelsTests.cs. The test seeds the database, calls the endpoint and asserts the response shape. UseCaseShapeTests will fail until this file exists.

4. Admin store and API composable

Add admin/src/stores/labels.ts with a fetchLabels action that calls runAction. Add admin/src/composables/apis/useLabelsApi.ts with a getLabels function that calls useApi.

5. Admin screen

Create admin/src/views/labels/LabelsView.vue. The view calls the store action on mount and renders the list. No direct useApi calls in the view.

6. Run the checks

bash
# backend (from docker/)
./scripts/manage.sh test

# admin
cd admin && npm run lint && npm run typecheck && npm test -- --maxWorkers=2
Was this page helpful?

Released under the AGPL-3.0 License.