Motivating A New Framework#

The last thing we need is another Web framework that rehashes the same design patterns and/or forces programmers to write similar boilerplate code.

Some history#

In the late 1990s, the thought of adapting the Model-View-Controller (MVC) pattern from Smalltalk as a design pattern for modeling Web applications in object-oriented programming environments. The advocates for the MVC pattern lauded its simplicity in imposing a proper separation of concerns.

Not long after the introduction and mass adoption of MVC as the “best practice” for organizing code in Web applications, Web programmers were been assailed with “best practices” trying to wed the complimentary philosophies of SOLID principles introduced by Bob Martin in 2000, and Domain-Driven Design introduced by Eric Evans in 2003. This has led to the adoption of architecture patterns such as Clean Architecture (primarily adopted in Java communities) and Onion Architecture (primarily adopted in C# communities).

Four concentric circles, outside in, Framework and Drivers, Interface Adapters, Application Business Rules, Enterprise Business Rules

Fig. 1 Uncle Bob’s Clean Architecture#

Four concentric circles, outside in, UI/Tests/Infrastructure, Application Services, Domain Services, Domain Model

Fig. 2 Jeffery Palermo’s Onion Architecture#

There is nothing wrong with these architectures if you must use an object-oriented programming environment. However, if you read any articles or watch any videos demonstrating how to implement the Clean or Onion Architectures, there is a proliferation of very tiny classes with little to no state; they are, instead, near implementations of functional programming ideas (message passing and isolation of side effects) in object-oriented languages. This begs the question: Why don’t we just move to functional programming languages to build Web applications?

While functional (and semi-functional) programming Web frameworks, such as Elixir’s Phoenix Framework and F#’s Saturn Framework, provide viable alternatives to object-oriented Web frameworks, they are still considered niche choices by most companies. One can argue that this is more about the inertia of corporate computing than the desire of programmers to work in safer, more comprehensible programming environments.

The modern Web framework#

Irrespective of the programming paradigm, functional or object-oriented, all Web frameworks generally have the same functionality. Let’s take a look at what they do and what they demand from us programmers.

The framework handles…#

Generally, all modern Web frameworks perform the following steps.

  1. Accept an incoming HTTP request from a client

  2. Parse the request line and header lines

  3. Determine how to resolve the request to a handler

  4. Structure the request metadata into some structure

  5. Determine the request’s actor

  6. Determine if the actor has the permission to invoke the handler

  7. Invoke a handler with cleansed values bound to the handler’s inputs

  8. Wait for the handler to complete

  9. Handle exceptions that occurred in the handler

  10. Stream the response to the client

The way the Web framework resolves what code should handle the request is usually determined by the programming paradigm. Object-oriented frameworks look for a controller class and invokes one of its methods. Functional frameworks finds a function that is usually a composition of the functions needed to handle the request.

The RESTrict Framework does not attempt to usurp this part of the modern Web framework. The initial version of the RESTrict Framework uses Starlette to manage all of that except the authorization security element.

The framework demands…#

In step 8 above, our code runs to make our Web application. What does our code do? In general, our code performs these types of actions in any order.

  • Validate input

  • Save data to a persistent store

  • Get data from the persistent store

  • Send messages to other systems

  • Send internal messages

  • Shape data into a response

  • Return the response data to the Web framework

This is where the RESTrict Framework steps in and handles most of this.

Most object-oriented Web frameworks encourage you to use some kind of object-relational mapper. BUT, the classes most often defined are data access objects (DAOs)which are really just a declarative syntax in the host programming language that defines the shape of the data. These DAOs also often have some kind of data validation based on constraints added as metadata on the property declarations in the class. Most of these DAOs are implementations of the Active Record design pattern. This requires us to mix our persistence strategy with the class definition.

Listing 3 Example DAO in Java with JPA and Hibernate#
@Entity
public class Book {
    @Id
    @GeneratedValue
    private Integer id;

    @Column(nullable = false)
    private String name;

    @ManyToMany
    @JoinTable(name="BOOK_AUTHORS",
    		   joinColumns=@JoinColumn(name="BOOK_ID"),
    		   inverseJoinColumns=@JoinColumn(name="AUTHOR_ID"))
    private Set<Author> authors = new HashSet<>();

    // getters and setters
}

C# developers that adhere to the Onion Architecture tend to not use those annotations (which are available to them). Instead, they use what they call “fluent configuration”.

Listing 4 Example DAO in C# with Entity Framework#
public class Book {
  public int Id { get; set; } = 0;
  public string Name { get; set; } = "";
  public ICollection<Author> Authors { get; } = new List<Author>();
}

public class BlogEntityTypeConfiguration
  : IEntityTypeConfiguration<Book>
{
    public void Configure(EntityTypeBuilder<Book> builder)
    {
        builder
            .Property(b => b.Name)
            .IsRequired();

        builder
          .HasMany(e => e.Authors)
          .WithOne(e => e.Book)
          .HasForeignKey(e => e.BookId)
          .IsRequired();
    }
}

Data validation elsewhere#

In these object-oriented Web frameworks, programmers are usually forced to duplicate any DAO data validation (uniqueness, min and/or max length, existence) in other places, generally on the methods on the “controller” classes, the handlers of an HTTP request.

Too much work#

So much of this can be automated. Microsoft has its Data API builder which exposes rows in database tables as resources. There are other tools and frameworks that do the same thing. Why not just live with that?

Resources are not records#

REST is about Representational State Transfer. What is the “representational” part of that? It is a representation of a resource. A resource is more than just data. It is the data and the interactions with the resource and the state side effects it makes.

This is a similar cry that object-oriented developers make: objects are data plus behavior!

But, a class is just a set of procedural blocks with hidden state. The reason we don’t like them is that when state changes inside the object, it is difficult to understand how that change occurred, that is, what is the call stack that mutated the state of the object.

Functional programmers can celebrate with their pure functions that input defines output without side effects. Until, of course, you get to whatever the equivalent of the IO monad is in the runtime. Or worse, those hybrid functional/OO programming languages that can make it nigh impossible to figure out how state mutated.

Resources do not have a one-to-one relationship with database records. They could. But, they shouldn’t be constrained that way out of the box.

A potential solution#

The RESTrict Framework helps address these shortcomings of modern Web frameworks, object-oriented or functional. The framework acknowledges…

  • …the declarative nature of resource data shape

  • …that persistence is an orthogonal concern

  • …side effects should be traceable through the application

Because the RESTrict Framework embraces these ideas, it defines a new Web application paradigm: functional-declarative hybrid resource-oriented development.