Here’s a new proposal to encapsulate Domain Layer.
Let me introduce the project called ‘Plastic’.
Currently, we have realized by programming history that it is very natural to design a software around Domain and take other details for the domain as external dependencies. Many application design topics such as DDD, EBI Architecture, and Clean Architecture all talk about it.
Since there are already a lot of sources and references on this, I won’t talk about it in depth.
And applications to implement the above-mentioned are usually implemented in the following form.
Type B API Layer placed on the right side of the figure can be called Domain Service Layer. It can be used for orchestration of Domain objects.
Plastic, which I would like to introduce, is a project to support this API Layer.
Plastic effectively encapsulates Domain Layer and supports exposure to various interfaces with Command Pattern, Source Generator and PipelineContext. (Source Generator enables source code automation)
Command Pattern
Command Pattern is a simple design pattern in which the object itself is a command.
In general, applications are very effective to be expressed in Command Pattern because it expose and receive specific commands to users. Reading, writing, and deleting, all these things can be a command.
In addition, since each Command has Single Transaction, it is very appropriate to use it as a unit to encapsulate Domain Layer.
This concept is already implemented by many developers under the name called Usecase or Interactor.
However, plastic has some differences.
First of all, it does not violate explicit dependencies principle. Request & Response is often used in CQRS implementations via Mediator Pattern for source code flexibility. this violates explicit dependencies. It refers to hiding the dependencies that are needed to operate the objects. Microsoft has posted a useful reference on this. Please refer to this link.
The command generated by Plastic is explicitly injected through the constructor.
class AddController : ControllerBase
{
public AddController(AddCommand addCommand)
{
…
var result = addCommand.Execute( … );
}
Another inconvenience of Mediator Pattern is that it is always necessary to create a new parameter object for the command. However, Plastic can designate a parameter type as Generic when creating a command. Of course, you can use Tuple.
In addition, Command in Plastic basically supports validation.
Below is the top-level abstract of the command.
public interface ICommandSpecification<in TParam, TResult>
{
Task<TResult> ExecuteAsync(
TParam param, CancellationToken token = default); Task<Response> CanExecuteAsync(
TParam param, CancellationToken token = default);
}
What’s unusual is that it’s inside the implementation.
Consumers can call ‘CanExecuteAsync(…)’ whenever they want to,
but also, this function is the first one to be called inside whenever ‘ExecuteAsync(…)` function is called.
This is to force validation before execution.
Source Generator
In an era where automation is essential, a source generator that can automate even source code has enabled to write flexible source code that were not possible in traditional programming. (Of course, this metaprogramming has been around for a long time.)
In fact, Plastic was a project that could be carried out because of this Source Generator.
Plastic uses Source Generator to support users.
This also made it possible to resolve the aforementioned explicit dependencies principle. Additionally, it supports many inconveniences that users experience.
You don’t have to register numerous commands in the IoC Container (Service Collection) one by one. You’ll already be ready to inject through the constructor just by writing the command.
It will be processed at the time of compilation.
I plan to start the Plastic.Binding Project (working title) in the future. This will generate source codes that automatically expose as communication protocol specifications such as Http and gRPC just by creating commands and specifying a few attributes. In the case of the CLI, I will make it easy to bind the command line, and in the case of the GUI, I have an idea to bind it to the ICommand.
PipelineContext
The idea of having pipelines between certain commands and bound paths, and running them in series every time began from Asp.net and have been maintained to MediatR, have been shown very effective results.
Plastic also supports this Pipeline concept. The characteristic of plastic is that it provides ‘PipelineContext’ to each pipe via Source Generator.
public class PipelineContext
{
public readonly object? Parameter;
public readonly Type CommandSpec;
public readonly IReadOnlyList<object> Services;
…
Each pipe can perform additional logic with this context, and you can find the interesting point at read-only variable named Serivces.
Services are instances of dependent objects that will be injected into the constructor of the Command that has been targeted to be executed.
Each pipe can access these instances before and after command execution to perform more diverse logic. The most representative scenario is Single Business Transaction. Changes in dependency due to Command can be handled at once, and if it failed, Command execution result can be processed as failure through PipelineContext.
Closing
The plastic project has just begun.
What was introduced in this post is the minimum unit useable in Plastic. You can automatically expose commands of application to various interfaces (CLI, GUI, Web API) via Plastic.Bindings in the future.
Thank you.