all Technical posts

A Happy Marriage in the Form of a C# & F# ASP.NET Core Application

In a previous post, we looked at how F# is an excellent language to build a ports & adapters architecture. In this post, we will look at how C# takes over the role of the port, while F# is still at the center of things: a.k.a., a happy marriage.

The restaurant core code, a.k.a. the domain

This post is a follow-up to a past post where F# was the language we used to build the entire application. We used Giraffe as a way to setup the ASP.NET Core infrastructure. This post, however, will only use F# at the core or domain of the application. This is the place where F# really shines. The goal of this post is to show that these two .NET languages work very well together, each in their respective field.

To start off, we can re-use our domain code from the previous post — I will not go over this code again. The important thing to know is that this code represents a simple reservation system for a restaurant, where each day can fill up with up to 20 people, spread across one or more reservations. The focused reader will see that it is not exactly the same code. It adds CompiledName attributes and uses methods like GetName to soften the interoperability towards C#.

For more information on these interop code techniques, see my other post: An F# Primitive Giraffe Wearing Lenses: A Ports and Adapters Story.

This post is not necessarily about this logic, but more about how this logic can be used in a C# environment with ease.

The translation layer, a.k.a. the adapter

The previous post talked about having an ‘adapter’ layer that translates the outside models (DTO and DB) toward the domain models. That has not changed: now, these models are expressed in regular C#.

This should not be new code, but I will show it as if it is, to be on the same page. Notice that I did not do anything special. This is roughly how these models would be defined.

To go from our port models toward our domain models, we can create an adapter instance that does this translation for us. Due to the way OOP applications are structured, these adapters will have to be injected into other classes for them to be tested properly.

The following code sample shows how the DTO request model can be translated toward the domain reservation model. Notice that the ValidationResult type is used when the translation happens towards the domain and the center of the application. This validation type is part of the FPrimitive package and is a C# alternative to the built-in F# Result type. The conversion happens automatically. Notice that the F# domain and the C# adapter do not have any additional conversion. The language barrier is completely gone, which is what you want when you incorporate these two languages.

The same kind of adapter can be made for the DB entity type.

In the same way, we can create an interface for our F# Restaurant module. As we use functions and not objects, it can be strange to use these directly in a C# application. To avoid confusion, we can create a proxy for the module and use it like an object. This extraction makes it testable from an OOP perspective.

This is probably the only real difference in F#/C# interoperability. This extraction may in certain places not hold much added value, as it is part of the service. To be fully testable, you may want to extract it like this, though.

💡 The System.Outcome model is also a model that is by default available via FPrimitive. It provides C# devs with yet another alternative to F#’s ‘Result’ model, if the result is not a validation result, like in this case: a reservation error and no validation error.

The ASP.NET Core application, a.k.a. the port

The ‘port’ is the part that talks to the outside world. In this case, we will use a minimized ASP.NET Core application with a single API controller that takes in our reservation request DTO (data-transfer object). The following code shows how everything comes together. This includes how the adapters translate the DTOs to the domain, how the domain runs its actions, and how the adapters translate the result back towards DTOs — making a true union architecture.

Usually, this direct-repository access is also hidden behind services to make the API controller methods more clean and readable, but for the sake of simplicity, all the needed objects are injected here. These services, however, could hide both the translation and the proxy, so that the inputs/outputs of the service are still port models.

💡 The Traverse is a functional concept for something called ‘foldable types‘. The result of the _repository.GetAll() is a sequence of ReservationEntity models and we need them all be transferred to a Reservation domain model. If we would do a simple map/select, we would end up with a sequence of ValidationResult models, while we actually want a ValidationResult of a sequence. That is what the ‘Traverse’ does for us. It is an extension available in the FPrimitive library and a well-known concept in the functional programming community.

Conclusion

Both C# and F# are .NET languages, but we do not often see them combined. My theory is that it is not because they are not compatible, but because the two camps do not see the benefits of each other. Most of the external .NET libraries are written for C# and I think that is not only because it is a more popular language, but also because it expresses side effects better. F#, on the other hand, does a far better job of expressing the domain and translation toward it. If we combine the two, we get the best of both worlds and get a secure and fun workplace for free.

Thanks for reading!
Stijn

Subscribe to our RSS feed

Hi there,
how can we help?

Got a project in mind?

Connect with us

Let's talk

Let's talk

Thanks, we'll be in touch soon!

Call us

Thanks, we've sent the link to your inbox

Invalid email address

Submit

Your download should start shortly!

Stay in Touch - Subscribe to Our Newsletter

Keep up to date with industry trends, events and the latest customer stories

Invalid email address

Submit

Great you’re on the list!