Modeling Services
The Services Designer in Intent Architect is a powerful tool for modeling "Application Services" in your applications. This module enables developers to define how an application can be interacted with at the service level, allowing for the creation of both internal services and publicly exposed endpoints.
What is an Application Service?
An application service is a layer in an application's architecture that serves as an intermediary between the domain layer (business logic) and the presentation layer (e.g., user interface or API). It orchestrates use cases and workflows, delegating detailed domain logic to the domain layer.
Key Characteristics of an Application Service
- Coordinates Use Cases: Encapsulates specific use cases or workflows, such as "Register a User" or "Place an Order."
- Delegates Domain Logic: Delegates core business logic to domain entities or domain services.
- Handles Input/Output: Processes input from the presentation layer (e.g., HTTP requests) and returns output (e.g., HTTP responses or data transfer objects).
- Transaction Management: Manages transaction boundaries, such as starting, committing, or rolling back database transactions.
- Separates Layers: Prevents the presentation layer from interacting directly with the domain layer, maintaining separation of concerns.
- Interacts with Infrastructure: Uses repositories, mappers, or other infrastructure components to fetch or persist data.
Service Modeling Paradigms
Intent Architect provides two paradigms for modeling application services: CQRS (Command Query Responsibility Segregation) and Traditional Services. This flexibility allows you to design services tailored to your system's architectural requirements, whether you prioritize scalability and clarity or prefer a unified approach.
Modeling services focuses on defining the flow of data into and out of your application (data contracts). You can also optionally model implementations for your services.
CQRS Paradigm
- Separates read and write responsibilities into distinct models optimized for their respective purposes.
- Commands handle state-changing operations, focusing on business logic and domain consistency.
- Queries handle data retrieval, often accessing read-optimized data stores or projections.
- Ideal for systems with complex requirements or high scalability demands.
- Use case-centric.
Traditional Service Paradigm
- Combines read and write logic into a single service.
- Simplifies development by using a unified data model and service structure.
- Common in systems with straightforward requirements or minimal scalability concerns.
Intent Architect enables you to effectively model services using either approach.
Exposing an Application Service
By default, application services are only available internally. To expose these services for external consumption, you must take explicit action. This involves making critical decisions:
- Which service endpoints will be exposed?
- Over which technology will they be exposed?
- What are the technology-specific configurations (e.g., security, addressing)?
The methods available for exposing services depend on the modules installed. For example, if you have the Intent.Metadata.WebApi
module installed, you can Expose as HTTP Endpoint to expose services over HTTP using REST conventions.
Creating a CQRS Command
- Add a
Command
to a diagram in the Services Designer. - Name the
Command
, typically suffixed withCommand
(e.g.,CreateCustomerCommand
). - Right-click the
Command
and select Add Property to define its data. - Add complex data types as needed:
- DTO for nested structures.
- Enum for enumerations.
- Optional: Define the return type of the
Command
in the property pane or by pressing F2.
Implementing the Command
Once applied to your codebase:
- Right-click on the
Command
and select Open in IDE -> {CreateCustomerCommandHandler.cs}. - Implement your business logic in the
Handle
method.
Note
Many service implementations are predictable and repetitive. Intent Architect can generate these implementations for you: Modeled Service Implementations.
Tip
Quickly model or bootstrap your services using the CQRS CRUD Accelerator.
Creating a CQRS Query
- Add a
Query
to a diagram in the Services Designer. - Name the
Query
, typically suffixed withQuery
(e.g.,GetCustomerByIdQuery
). - Right-click the
Query
and select Add Property to define its data. - Add complex data types as needed:
- DTO for nested structures.
- Enum for enumerations.
- Select return type of the
Query
in the property pane or by pressing F2 (typically aDTO
).
Implementing the Query
Once applied to your codebase:
- Right-click on the
Query
and select Open in IDE -> {QueryHandler.cs}. - Implement your business logic in the
Handle
method.
Note
Many service implementations are predictable and repetitive. Intent Architect can generate these implementations for you: Modeled Service Implementations.
Tip
Quickly model or bootstrap your services using the Traditional Service CRUD Accelerator.
Creating a Traditional Application Service
To create a service with operations:
- Right-click on the diagram and select New Service, then provide a unique name.
- Right-click the service and select Add Operation, then provide a name.
- Right-click the operation and select Add Parameter. Specify its name and type. If it represents an inbound payload, select the corresponding DTO.
- Leave the type as
void
for operations with no return value, or choose an appropriate return type.
Implementing the Service
Once applied to your codebase:
- Right-click the
Service
and select Open in IDE -> {OrganizationsService.cs}. - Implement your business logic in the method corresponding to your modeled
Operation
(e.g.,CreateOrganization
).
Note
Many service implementations are predictable and repetitive. Intent Architect can generate these implementations for you: Modeled Service Implementations.
Tip
Quickly model or bootstrap your services using the Traditional Service CRUD Accelerator.
Creating a DTO
To create a DTO:
- Right-click on the Service Package or a containing folder and select New DTO, then provide a unique name.
- Right-click the DTO and select Add Field. Specify the name and type.
Inheriting from a DTO
To inherit one DTO from another:
- Right-click on the DTO that will inherit and select New Inheritance.
- Select the parent DTO.
Mapping an Outbound DTO
To map outbound DTOs:
- Right-click on the DTO that will receive the mapped information and select Map From Domain.
- In the dialog, specify the domain entity and select the attributes to include in the outbound DTO.
- Check the desired attributes and click Done. This links your domain data to the DTO.
Adding a Diagram to the Services Designer
To enhance visual organization:
- Right-click on the Services package and select New Diagram.
- If the designer was in Tree-view, it will switch to a diagram view.
- Rename the diagram by right-clicking it in the Tree-view and selecting Rename.
- Drag services from the Tree-view onto the diagram to create visual representations.
- Optionally, create multiple diagrams for different perspectives. The Tree-view remains the source of truth.
Tip
Hold down CTRL while dragging elements from the Tree-view to include directly associated elements.
Using Accelerators to Rapidly Model Services
Accelerators are macros or scripts that automate repetitive modeling tasks, saving time and ensuring consistency.
Create CRUD CQRS Operations Accelerator
This accelerator models a CQRS service with a CRUD implementation, including the following:
- Commands:
- Create Entity Command
- Update Entity Command
- Delete Entity Command
- Queries:
- Get Entity by Id Query
- Get All Entities Query
Commands
based on theEntity
's operations.
- Right-click on the Services Package, and select Create CRUD CQRS Operations.
- Select the domain
Entity
to model the service around.
Note
You can also run this accelerator on a Folder in the Services Designer.
Create CRUD Traditional Service Accelerator
This accelerator models a Traditional Service with CRUD implementations, including the following operations:
- Create Entity
- Update Entity
- Delete Entity
- Get Entity by Id
- Get All Entities
Operations
based on theEntity
's operations.
- Right-click on the Services Package, and select Create CRUD Traditional Service.
- Select the domain
Entity
to model the service around.
Paginate Accelerator
Pagination enables large datasets to be returned in smaller, more manageable chunks. The Paginate
accelerator is available for any Operation or Query that returns a collection.
- Right-click on the qualifying Operation or Query.
- Select the Paginate menu item.
Actions performed by this accelerator
- Changes the return type of the
Operation
orQuery
fromTReturnType
toPagedResult<TReturnType>
. - Adds three parameters/properties to the
Operation
/Query
:- PageNo: Specifies the page number to retrieve, based on the PageSize.
- PageSize: Specifies how many records should be included in a single page.
- OrderBy: Specifies how data should be sorted before pagination. This is optional, and defaults to database ordering if omitted. It will order data in ascending order by default.
Note
If using the default CRUD implementation, the PageNo
parameter is 1-based by default (first page = 1), renaming the parameter to PageIndex
will make it 0-based (first page = 0).
Tip
The CRUD modules treat the OrderBy
as a dynamic LINQ statement. The OrderBy
parameter supports, a single entity property (e.g., name
), multiple entity properties (e.g., created, name
), sorting directions for each property (e.g., name desc
, created desc, name asc
).
Examples of valid OrderBy
formats:
name
name asc
name desc
created, name
created desc, name asc
Modeled Implementations
Create Entity Action
This action allows you to model the creation of a domain Entity
(Class
) using either an Object Initializer
or a Constructor
.
It can be applied to a Command
, a service Operation
, or a Domain Event Handler Association
(referred to as the Element
below).
Creating a Domain Entity using Object Initialization
- On a diagram, select Add to Diagram and choose the domain
Entity
you want to create. - Right-click on the
Element
and select Create Entity. - Connect the
Element
to theEntity
by left-clicking it.
This opens the Create Entity Mapping
dialog to map data from the Element
to the Entity
.
- Double-click the
Entity
in the right-hand panel.
A purple line appears, indicating the creation of theEntity
using anObject Initializer
. - Map data from the
Element
to theEntity
:- Double-click the
Entity
again to map all attributes and add missing ones. - Double-click an
Entity
attribute orElement
property to automatically map (or create and map) them. - Drag an an
Entity
attribute orElement
property to it's counter part*, to map them. - Drag multiple mappable items from either side to the other side's background, this will batch map the items, adding items if applicable.
- Double-click the
Tip
If no mappable property exists, you can define an expression (e.g., true
, 0
, ""
) in the text box next to the attribute.
Tip
To revisit the mapping screen, right-click on the Create Entity Action
or the association linking the Element
and Entity
, and select Map Entity Creation.
Creating a Domain Entity using a Constructor
- On a diagram, select Add to Diagram and choose the domain
Entity
you want to create. - Right-click on the
Element
and select Create Entity. - Connect
Element
to aConstructor
on theEntity
Left-click on theConstructor
.
This will open the Create Entity Mapping
Dialog, this dialog helps you map data from the Element
to the Entity
.
Double-Click the
Constructor
in right hand panel. This will add purple line between theElement
and theConstructor
, this represents how theEntity
will be created i.e. using thisConstructor
.Map how the data from the
Element
to theConstructor
, this can be done in several ways:- Double-click the
Constructor
again, this map all theConstructor
parameters to the correspondingElement
properties, adding missing ones where required. - Double-click an
Constructor
attribute orElement
property to automatically map (or create and map) them. - Drag an an
Constructor
attribute orElement
property to it's counter part*, to map them.
- Double-click the
Tip
If no mappable property exists, you can define an expression (e.g., true
, 0
, ""
) in the text box next to the attribute.
Tip
To revisit the mapping screen, right-click on the Create Entity Action
or the association linking the Element
and Entity
, and select Map Entity Creation.
Update Entity Action
This action models updates to a domain Entity
(Class
) using its Attributes
or an entity Operation
.
It applies to a Command
, a service Operation
, or a Domain Event Handler Association
(referred to as the Element
below).
Updating a Domain Entity Using Its Properties
- On a diagram, select Add to Diagram and choose the domain
Entity
you want to update. - Right-click on the
Element
and select Update Entity. - Connect the
Element
to theEntity
by left-clicking theEntity
.
This opens the Update Entity Mapping
dialog, where you can:
- Map data from the
Element
to theEntity
:- Select
Entity
attributes, drag them to the background of the left-hand side, mapping theEntity
attributes to the correspondingElement
properties. - Double-click an
Entity
attribute orElement
property to automatically map (or create and map) them. - Drag an individual
Element
property onto aEntity
attribute, this will map the two elements.
- Select
- Click Map Entity Query to define how the
Entity
should be retrieved:- Map the primary key of the
Entity
to correspondingElement
properties.
- Map the primary key of the
Tip
Revisit the mapping screen by right-clicking the Update Entity Action
or the association and selecting Map Entity Update.
Updating a Domain Entity using a Domain Entity Operation
- On a diagram, select Add to Diagram and choose the domain
Entity
you want to create. - Right-click on the
Element
and select Update Entity. - Connect
Element
to anOperation
on theEntity
by left-clicking theOperation
.
This opens the Update Entity Mapping
dialog, where you can:
- Map how the
Operation
will be invoked :- Double-click the
Operation
in right hand panel. This will add purple line between theElement
and theOperation
, this represents the invocation of theOperation
. - Map data from the
Element
to theOperation
:
- Double-click the
Operation
again, this map all theOperation
parameters to the correspondingElement
properties, adding missing ones where required. - Double-click an
Operation
parameter orElement
property to automatically map (or create and map) them. - Drag an individual
Element
property onto aOperation
parameter, this will map the two elements.
- Double-click the
- Click Map Entity Query to define how the
Entity
should be retrieved:- Map the primary key of the
Entity
to correspondingElement
properties.
- Map the primary key of the
Tip
Revisit the mapping screen by right-clicking the Update Entity Action
or the association and selecting Map Entity Update.
Delete Entity Action
This action allows you to model the deletion of a domain Entity
(Class
).
It can be applied to a Command
, a service Operation
, or a Domain Event Handler Association
(referred to as the Element
below).
- On a diagram, select Add to Diagram and choose the domain
Entity
you want to delete. - Right-click on the
Element
and select Delete Entity. - Connect the
Element
to theEntity
by left-clicking theEntity
.
Note
For Domain Event Handler
you will need to configure how the Domain Entity is queries, simply right-click on the Delete Entity Action
(Connecting Association) and select Map Entity Filter and map the domain Entity
s primary key.
Query Entity Action
This action allows you to model the querying of a domain Entity
(Class
).
It can be applied to a Query
, Command
, service Operation
, or Domain Event Handler Association
(referred to as the Element
below).
How to Query a single Entity
- On a diagram, select Add to Diagram and choose the domain
Entity
you want to query. - Right-click on the
Element
and select Query Entity. - Connect the
Element
to theEntity
by left-clicking theEntity
. - Select the
Association
you just created (Query Entity Action
). - Right-click and select Map Entity Query.
This opens the
Query Entity Mapping
dialog, where you model the criteria for Entity Select: - Map the filter criteria for querying your Entity, here are a few ways you could do this
- Double-click the primary key of the
Entity
. - Map the primary key of the
Entity
to correspondingElement
properties. - Map one or more
Entity
attributes, which uniquely identify theEntity
, to correspondingElement
properties.
- Double-click the primary key of the
- Click Done to complete the mapping (even if no filter criteria are applied).
How to Query a Collection of Entities
- On a diagram, select Add to Diagram and choose the domain
Entity
you want to query. - Right-click on the
Element
and select Query Entity. - Connect the
Element
to theEntity
by left-clicking theEntity
. - Select the
Association
you just created (Query Entity Action
). - In the Properties pane, check Is Collection (shortcut:
Alt + C
). You can also rename the Name entity to "entities" or similar (this will be the name of the variable the query results are assigned to). - Right-click the association and select Map Entity Query.
This opens the
Query Entity Mapping
dialog, where you model the criteria for Entity Select: - Optionally Map Filter criteria for the
Entity
selection. - Click Done to complete the mapping (even if no filter criteria are applied).
How to apply mapped filters to a Query
- On a Query that has a field used for filtering, right click on the Query action and select Map Entity Query. This opens the
Query Entity Mapping
dialog. - Map one or more
Entity
attributes, which uniquely identify theEntity
, to correspondingQuery
properties. You can double-click on anEntity
attribute to create a corresponding field on theQuery
element that is also mapped. - Click Done to complete the mapping.
Note
Currently, filter criteria mapping only supports "==" conditions. More complex filters must be implemented in code.
Service Returns and Query Entity Actions
Query Entity Action
is designed specifically for modeling the querying of data and is not directly tied to the Return Type of the Element
it is modeled on.
Suppose you are modeling a Query
named GetCustomersQuery
. This query would likely return a collection of CustomerDto
s. The implementation of the GetCustomersQueryHandler
would involve the following steps:
- Query the database to get a list of
Customer
s. - Transform the
Customer
s toCustomerDto
s and return theDTO
s.
The Query Entity Action
focuses solely on step 1 — querying the database. It is unrelated to step 2, which involves transforming the data into a different return type.
When using Query Entity Action
alongside our CRUD modules, these modules employ heuristic algorithms to handle the wiring for step 2 automatically.
This mapping can be established using the Map From Domain context menu option on the DTO
. This ensures that the transformation from the queried type (Customer
) to the return type (CustomerDto
) is recognized and automated.
The return type and query type must also share the same value for Is Collection. If the query retrieves a collection of entities, the return type should also be a collection. Similarly, if the query retrieves a single entity, the return type should not be a collection.
Call Service Operation Action
The Call Service Operation Action allows you to model the invocation of Service
or Domain Service
operations.
It can be applied to a Command
, Query
, service Operation
, or Domain Event Handler Association
(collectively referred to as the Element
below).
- On a diagram, select Add to Diagram and choose a
Service
orDomain Service
you want to invoke. - Right-click on the
Element
and select Call Service Operation. - Connect the
Element
to theOperation
you want to invoke, by left-clicking theOperation
.
This opens the Service Operation Mapping
dialog, where you can:
- Map data from the
Element
to theOperation
invocation:- Double-click the
Operation
again, this map all theOperation
parameters to the correspondingElement
properties, adding missing ones where required. - Double-click an
Operation
attribute orElement
property to automatically map (or create and map) them. - Drag an an
Operation
attribute orElement
property to it's counter part, to map them.
- Double-click the
Processing Actions
Warning
This is an experimental feature and likely to change in the future
Add Processing Action allows you configure additional implementation details through additional mappings. These mappings are able to link any existing actions together.
Here are a few examples of what is possible.
- Invoke an Operation on a Domain Entity you are updating.
- Map data from the result of a Service Operation Call onto an entity.