Service basics
This article provides an overview of the elements related to your technical documentation which all lives in the Service editor in Uniscale.
Last updated
This article provides an overview of the elements related to your technical documentation which all lives in the Service editor in Uniscale.
Last updated
In Uniscale, services are created for two possible uses. For a solution or as a standalone service.
Solution-owned-services
are, as the name suggests, services created only to provide functionality within the bounded context of a solution. Their functionality is exposed via revisions to all the modules inside the owning solution.
Standalone services
are the specification of an individual service without any links to its surrounding eco-system.
Services are multifaceted, incorporating various elements that work cohesively to deliver functionality. This article delves into the primary components involved in service design, their application, objectives, and underlying principles. By dissecting these elements, we aim to provide a clearer understanding of what goes into creating a robust service.
Namespaces play a crucial role in the hierarchical organization of your service. Initially, they function to provide a structured framework. However, as your library evolves, namespaces become pivotal for identification purposes.
Because we want to enable you to express your domain you should make sure that the hierarchy and naming of your namespace reflect the problem domain you intend to fulfill through this service.
Namespaces are subject to the locking/unlocking/readying process within a revision. This means that editors of a service can manage the content via each of those statuses as needed.
The name of a namespace is strictly managed through locking and unlocking while the description is easily editable. The reason is that the namespace forms the location of endpoints and data contracts all the way into the SDK so changing a namespace name structurally changes the location of things in the SDK.
Use cases (UC) come in two forms Functional
and technical
.
Functional UCs paint a more general picture of how a user might interact with your business to reach their goals. Instead of focusing on technical detail, it’s a cause-and-effect description of different inputs.
This type of UC is encountered and created from inside the specification.
In Uniscale, the main differentiation factor for the UCs is their technical intention.
The difference is Functional use cases are defined in and owned by the module as it is describing end-user functionality. The functional use cases can be connected to either standalone or technical use case flows. However, only standalone and technical use case flows are defined within the Service To Module.
The technical use case does not describe how the end user interacts. It rather is a group of flows that the services expose as a larger group of functionality that belongs together. For instance, a use case for a service could be mail-sending management where it exposes flows for sending mail, checking the status of sent emails, and for instance hourly stats.
It does not describe the end-user functionality, that will always be described through a module but it describes a larger set of technical functionality that the service wants to expose to its users (developers for other services/frontends).
UCs are subject to the locking/unlocking/readying process within a revision. This means that editors of a service can manage the content via each of those statuses as needed.
Different types of flows (technical/standalone) form the specification of endpoints. They represent the intention of use of an endpoint and if an endpoint is not linked into at least one flow it will not become part of the SDK as it has no intention of use.
Use case flows (UCF), as the name suggests, can be used in one or more functional use cases if service to a module or as a single standalone flow against another service (if service to service)
A functional UCF contains:
a title
a Rich Text description
Possible functional Acceptance criteria
and a list of linked endpoints
They can have any technical intention, as they are a clearly defined larger use case that is owned by the service itself.
A technical UCF contains:
a title
a Rich Text description
possible Technical Acceptance criteria
and a list of linked endpoints
UCFs are subject to the locking/unlocking/readying process within a revision. This means that editors of a service can manage the content via each of those statuses as needed.
In Uniscale, we follow the same understanding and concept around endpoints as the industry.
We must first distinguish between APIs and Endpoints as those can be quite familiar terms, with slight differences though.
It’s important to note that endpoints and APIs are different.
An endpoint is a component of an API, while an API is a set of rules that allow two applications to share resources.
An Endpoint is the definition, structure, and traits of a service's exposed functionality. An API is the concrete implementation of that Endpoint determining location and means of transport. An endpoint is abstract API is concrete.
For example, an Endpoint exposed over HTTP is an HTTP API. An endpoint handled in a Uniscale Interceptor is a Uniscale API
As such, coming back to the definition of endpoints, they represent the configuration of the expected behavior of the API, together with the expected payloads for the possible request and response. Quite a bit to take in so let's break that down.
An endpoint will be defined by:
behavior type: message or Request response
owning namespace (location in SDK structure)
behavior ( create, read, etc)
name ( predefined based on behavior or custom )
description optional
Depending on the type, an endpoint can contain one ( Message) or two payloads (Request - Response):
Once your endpoints are configured and set up with their payloads, they can be enriched with more configuration on how they will interact with outside systems or requests.
Initially, every endpoint assumes a happy scenario where no complications or bad scenarios can occur. The reality is different, of course, and for various reasons, your frontend can present various scenarios where the data is wrong, or missing, or the backend cannot process the requested information.
In Uniscale you can enrich each endoints with direct Error codes to cover those exact scenarios. For each error code, you can provide:
a system code
a user message
If you use the Uniscale session and interceptors the error response management is automatically handled through call chains. This means a frontend calling a service, calling another service.
Structuring your data can be done in multiple ways in Uniscale, and as such we have provided various top-level "containers" or "contracts" to manage their scope, and bounded context, in this way facilitating easier management of their Single source of truth.
Data contracts are subject to the completion process within a revision. This means that editors of a service can manage the content via each of those statuses as needed.
Within DDD, aggregates represent the main data containers for your properties and data modeling. They are mainly characterized by a Single source of truth for individual properties and act as an umbrella for all the values within the same concept. The main definition of an Aggregate is that it is data that is referenceable by an identifier.
Aggregates are unique by identifier, which is automatically generated based on the aggregate name. As such checking if two aggregates are equal is done by comparing their identifiers.
Aggregates are placed under a namespace which acts as its owner.
Example of aggregates:
User
Person
Order
In Uniscale, aggregates have by default an aggregate identifier created automatically which follows the aggregate name. Afterward, an aggregate can be expanded with a flat or nested structure of properties.
Value objects represent data contracts that are slightly different from aggregates. They are unique in content and as such they can be quite simple in structure or even standalone.
Value objects are placed under a namespace which acts as its owner.
Example of value objects:
Address
SearchPayload
There are 2 types of value objects
type alias: a type alias is a native type with a specific meaning (ex. Gender). For instance, if an endpoint wants to use a native type as its input/output it must declare a type alias value object and use that.
type structure: a nested structure of properties (ex. SearchPayload). This is encountered in situations where the request of an endpoint is a fixed structure that is always the same but it is not necessarily an aggregate.
Value objects can be referred to as other aggregates, value objects, and property groups. Editing them anywhere in the interface will also update the source of truth at its origin.
Once further into your modeling, you will reuse certain properties from across various aggregates and value objects, also with different cardinalities in mind. Exploring such scenarios, we can have:
Example:
Property groups can be explained as a way of composing data from different value objects and aggregates to be used in Endpoint payloads. The only place and main place to use property groups is as a payload for endpoints, being in a Message or a Request-response situation.
A native data type is a classification of data that tells the library generator how the library intends to use the data. In Uniscale we provide all the most commonly used data types. They are:
GUID
a uniquely generated identifier
c5f364fa-1d39-4de5-abdf-c1e6a54d05a6
terminology
Collection of terms
{
dk :"Denmark",
no: "Norway",
...
}
date
Day-month-year
2022-09-27
datetime
Date with timestamp
2022-09-27 18:00:00.000
boolean
logical true or false
true, false
float
fractional numbers
64bit floats
integer
whole number
64bit integers
string
A sequence of characters
"hello world"
aNote: UTF is used as a variable-length character encoding standard.
At its baseline, every functionality in a service should exist for a purpose and intent. That intent can be internal or external. Towards functionality (solution or module), or another system ( service or infrastructure)
Service to module
All the functionality a service provides means to be exposed to a Frontend, a module, or a solution.
Service to service
All the functionality a service provides means to be exposed to a system, in the shape of another service can sometimes be referred to as Backend communicating with another backend.
Service to infrastructure
All the functionality a service provides means to expose an infrastructure as a whole, usually Storage, Networking, or any other IaaS scenarios.
Only standalone use case flows and technical use cases in a service are governed by technical intentions.