2

I'm building a lot web applications for internal use, my setup is basically ASP.NET Core, React/Angular and Typescript.

I started to find my self duplicating a lot of code from C# into TypeScript types and found this nice little utility https://github.com/pankleks/TypeScriptBuilder that will create TypeScript-models from my code.

This got me thinking about how we communicate with our backend, up until now it's been a matter of creating "REST-isch" WebApiController's to expose data, more often than not these endpoints is more or less tailored to a specific view and the actions that can be take from this view. We have never reused the same WebApi for both our own app and any external interaction, the external API would be it's own thing.

So... after thinking about this I started to test an idea that I got, what if every query and command from the front end could be modeled with a C# "request" and "response"-type that can be shared between frontend and backend? So I made a proof of concept.

My C# code has something called a "FrontendAction" that can be either a command or a query, the FrontendAction must have a "request"-type (incoming information) and a "return"-type (data returned by the action).

Then I've created one WebApi-endpoint ActionController with one method: Execute that takes in the request-object, figures out which FrontendActionHandler (C#-class for either a command or request) that should take take of it, execute this handler and returns the "response" object.

This way I can have strongly typed commands / queries in my TypeScript-code that gets genereated (kept in sync) any time I create/change a C#-frontend action. There is almost zero bolierplate (aka. creating WebApiController, methods, actions, and then ajax-code to call the different controllers/methods).

Examples of Queryes:

  • GetCustomOverview (parameters: customerId)
  • GetCustomerOrders (paramerters: customerId, orderStatus, page)

Examples of Commands:

  • SaveCustomer (parameters: CustomerDetailsDto)
  • AddOrder (parameters: AddOrderDto)

Have anyone done something similar? How does this approach sound to you?

1
  • What benefits has your proof of concept identified? Apr 30, 2021 at 15:35

1 Answer 1

1

Then I've created one WebApi-endpoint ActionController with one method: Execute that takes in the request-object, figures out which FrontendActionHandler (C#-class for either a command or request) that should take take of it, execute this handler and returns the "response" object.

This is the mediator pattern and is incredibly common to use in conjuction with CQRS. I've not yet come across a CQRS implementation that did not in some way use a mediator.
I suggest taking a look at Mediatr. While it's not the only mediator library in existence, it seems to be the predominant favorite in terms of popular usage.

The only difference here is that you're merging your mediator with your controller. And this is where my internal red flags start popping up.

Condensing two distinct responsibilities into one is always a shifty proposition, because it inherently opens you up to painting yourself in a corner, much like most bad practice coding does.
If you merge the layers, only to later realize that you (now) benefit from their separation, you're going to have to spend a lot of time and effort to separate these now merged layers.

So the question becomes: is there value to separating the mediator from the controller?

While I can't conclusively prove that there must be value there (I can't cover every possible use case), I'm also unable to find any case in which there will never be any value there (because requirements are always prone to change over time).

We can explore that evaluation deeper, but given the currently unanswered nature of the problem, it is better to err on the side of clean code and therefore keep them separate.

This way I can have strongly typed commands / queries in my TypeScript-code that gets genereated (kept in sync) any time I create/change a C#-frontend action.

It might be easier to showcase an exact example of what you intend.
Irrelevant to the point mentioned below, this framework also uses Mediatr in the backend, so you have an example of that as well.

The linked project, Jason Taylor's approach to Clean Architecture, has a post-build step which generates TypeScript services which precisely mirror the endpoints of the controllers, thus ensuring that the frontend developer never needs to manually refer to an URL endpoint or deserialize a response, it's already available in premade TypeScript services which act as the perfect complement to the controller endpoints that the backend developer created.

Note that this of course also generates the TypeScript class definitions for all types that are used in the request/response models of the API's endpoints.

I work with this framework in my personal projects and it really is a dream. I make an adjustment to my endpoint (i.e. controller), build the project, and the TS (including Intellisense) has been updated and shows my new/changed endpoints, ready to be consumed in the frontend.

There are different ways to achieve automation. Your idea (the mediator) would be to bottleneck everything over the same controller so you don't have to keep creating controllers and TypeScript services.

Jason Taylor's idea was to retain the individual controllers, thus retaining more control over your endpoints, but automating the generation of the TypeScript services whose content is based on those controllers. Same automation, more granular control.

In other words, you don't need the mediator pattern if your intention is to automate your TypeScript-driven endpoint consumption.

2
  • Thank you! I do know that this is similar to CQRS, I use Mediatr on the back end a lot. This idea is more of a mediatr for front end request, at the moment I think of it as a "hardcoded" GraphQL that is custom for a given UI. I had a looked at the example project, and yes to be fair the biggest upside for me is that the models and services is generated. If this goes via different controller end points or not does not really matter. But as these CQRS-commands is front end only I don't really think that they are coupling the layers, the backend has it's own Commands/Queries. May 21, 2021 at 9:10
  • @MarkusKnappenJohansson: The mediator pattern works on any layer you want it to. All it really does is allow you to break down your layer into individual actions and easily resolving the right action for the right input; as opposed to categorizing your actions in little bundles (services) that contain a thematically linked set of actions. You can do that at any layer. I don't quite see the need to do that on the frontend api-services (as they could just neatly map to the existing controller endpoints), but if you feel differently, go for it.
    – Flater
    May 21, 2021 at 10:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.