Fitting the Pieces Together

Ody Mbegbu
Real World F#
Published in
4 min readSep 18, 2016

This is part of the Series: “Building Real-World Applications in F#”

Thinking about how to and where to put pieces together for the Dumia Application is easily the hardest part of putting this application together. Coming up with a nice repeatable way to piece together an application using Functional Language like F# requires a real change of thinking.

Paradigm Shift — Encapsulation vs Isolation

Before we go into the details of assembling an Application with F# or any other true functional language for that matter, We need to understand the fundamental difference in philosophy between the Object Oriented world and the Functional World. This can be distilled down to two concepts.. Encapsulation and Isolation

Encapsulation

You see in OOP Land, you had a Box of tools (Classes) that had specific uses.

Object Oriented Programming

Each tool had inner workings and behavior that you the user is not meant to see. Sort of like a black box. The user needed to know

  • How to instantiate it
  • What method(s) to call
  • What order to do the above

We would then assemble an application by connecting objects together either by letting objects create other objects or calling other objects. Because of this the user’s behavior depended on the expectation of the Objects it depended on. As a result of this, In practice, they were difficult to isolate which by definition meant that they were difficult to test because you needed to isolate code to test it.

To solve this, we relied on using Interfaces and Mocks to simulate not just the behavior but the character of the dependent object. These set of solutions led to the SOLID Principles of OOP Design. These exist primarily to make OOP code testable.

Isolation

With functional programming, You are dealing with Functions as the basic building block. Each function can be thought of as a lego brick

Functional Programming

A pure function is by definition isolated. Unlike Encapsulation where an Object is trying to hide things from you, a pure function can not do anything It did not declare in its interface (or signature). You could say that a Pure function is “honest”

This causes a paradigm shift because You need to think of solving problems by breaking them down into these small isolated functions and then re-assembling them at your application entry point.

This might seem counter-intuitive at first but then when you open you mind to the possibilities, It fundamentally changes how you think about building software.

Lego Mega Structures built one brick at a time

Because of my extensive background in Object-Oriented Languages, this paradigm shift produced so much Cognitive Dissonance about how to structure the Dumia Application that I literally had a sleepless night trying to resolve my old habits with the new paradigm

At Several Points in the design, I had to step back and think:

Everything is a Function

By taking this approach to application architecture, a lot of complexities fall away. You no longer need Interfaces, Mocks, Managers, Services, Presenters, Controllers and the list goes on. In fact, this image by Scott Wlaschin pretty much hits the nail on the head.

Taken from F# for Fun and Profit

Having this in mind, I came up with a very simple functional architecture.

I was able to categorize functions into two camps and as such put them in two assemblies

  • Domain — This contains functions that are “pure” business logic (or rules) which means that they don’t depend on the application environment (Web, Console, Mobile etc). These functions are typically pure and they just deal with transforming inputs and producing outputs. You should be able to take this code and move it from one environment to another easily
  • Infrastructure — This group contains functions that need to either talk to the external world (e.g. DataAccess functions or SMS/Email functions) or work with external dependencies. (e.g. Cryptography library)

The beauty of functional programming is that you can plug these two together using just their function signatures as the interface between them.

An example of this is using Higher-Order functions (functions that take other functions as parameters) as a form of Polymorphism rather than interfaces.

For example instead of having an IEncryption interface that has an Encrypt function that takes a string and returns a string, It will be replaced with a function that takes a string and returns a string. This function is passed into any function that needs to encrypt strings.

See.. No IOC Containers/Interfaces needed.

Doing these, I ended up with something like this

New Project Structure

--

--