Skip to main content

View - A Functor for Web App Design

This blog is about practical applications of Category Theory to the development of Java + Spring applications. I am looking at a design approach to simplify the development of web applications. Traditionally, this kind of back-office application is based on the Web 1.0 technology stack, using Spring Boot and Thymeleaf. My approach is to keep using Spring Boot but replace the generation of HTML with J2HTML and higher-order views.

From a Category Theory point of view, we can look at web applications as mappings from the Category of Business Entities and the Category of UI Widgets. If we go one step further, both business entities and UI widgets are mapped to Java classes. Thus, we can view a web application (or a part of it) as an endofunctor in the Category of Java Classes.


We define the View-functor as follows:

  • domain(V) - Java classes representing business entities - e.g., Invoice, User - and,

  • codomain(V) - Java functions that render the business entity as a DomContent object (DomContent is the J2HTML representation of a browser’s DOM node in the document)


            In Java, the codomain is all the classes that implement the following interface:

@FunctionalInterface

public interface View<T> {

    public DomContent render(T t);

}


A specific application defines the mapping between the domain and codomain.


Note: a Functor in Category theory has a formal definition. Our use of the term is more relaxed but can be formalized. For example, the mapping defined here is not a pure-function in the mathematical sense because there can be many views for the same Java class. For the class String we can have a View that renders it as a paragraph (tag <p>) or preformatted (tag <pre>).

View Composition


The benefits of the View Functor become evident when creating complex views out of simple Views. For example, to show the string “Something, something, something” on a page:


Given the view of a type T (in this case String) that has a mapping of View<T>, we can create another View that is the representation of T as a “card” and another one as a “page.” This example shows the composition of three functions:




View of Optional


Another example is combining the View functor with other functors, such as Optional.



Then, the view of Optional<V> is:

    static public <T> View<Optional<T>> ofOptional(View<T> uv, Tag<?> c) {

        return (Optional<T> ou) -> ou

            .map(uv::render)

            .orElse(c);

    }


Consider the case of building a page to display the user information: if the user is logged in, show the user’s name, and if it  is not logged in, display a login message:

        View<UserDetails> vu = u -> p(u.getUsername()); // when user is logged in

        PTag TAG_NOT_LOGGED_IN = p("Not Logged In"); // message to login

        View<Optional<UserDetails>> vou = Views.ofOptional(vu, TAG_NOT_LOGGED_IN);


The ability of the view functor to compose with other views and combine with other functors is the major strength of this design approach. We can focus on writing the code for the typical case - rendering the User, Invoice, and Payment entities - and let the framework deal with the messy parts like optional, errors, etc.

View of List

Another example where View functor proved helpful was in rendering a list of entities given the rendering of an entity as a row. The View of a List is the composition of the View of the Entity with an HTML container tag:

    static public <T> View<List<T>> ofList(View<T> vt, ContainerTag<?> ct) {

        return (List<T> lt) -> ct.with(

            TagCreator.each(lt.stream().map(vt::render)));

    }


ContainerTag is a J2HTML class corresponding to DOM nodes that can have children.


With this generic definition, we can define a view for a list of strings either as an unordered list tag with items (ul/li):

        List<String> l = Arrays.asList("a", "b", "c");

        View<List<String>> ulView = Views.ofList(s -> TagCreator.li(s), new UlTag())

         ulView.render(l); // returns <ul><li>a</li><li>b</li><li>c</li></ul>

Or, as an HTML table:

        View<List<String>> tableView = Views.ofList(s -> tr().with(td(s)), new TableTag());

Conclusions

In this blog, I defined a functor called “View” and showed how to use it to build complex pages using composition and interactions with other functors.

Next Steps

I will continue to refine the View framework. The directions that I have in mind are:

  • Closer integration with the Spring framework - dependency-injected views. For example, the AccessControlProvider could be a Spring Bean

  • Cover the semantic gap between Spring MVC and this View framework

  • Scripted views - delegate some rendering functionality to the browser with styles and scripts. For example, rendering table pagination in Javascript



Popular posts from this blog

Performance Testing a New CRM

Performance testing  is challenging, frustrating, often underestimated typically getting attention only after an incident. How did we the performance test and what did we learn during the development and implementation of  web-services for a new CRM system?

What Can Category Theory Do for Me?

Category Theory is one of the hot topics in computer science. There are many blogs, youtube videos and books about it. It is an elusive subject, with the potential to be the ultimate unifier of everything (math, quantum physics, social justice), to allow us to write the best possible programs and many other lofty goals. I decided to explore the field and see how it can help me. Below is a summary of 12 weeks of reading and watching presentations about Category Theory. It took me some time to select the study material. In the end I decided to use David Spivak’s book (Category Theory for Sciences, David Spivak, 2014) and the youtube recording of a 2017 workshop. These provided both a rigorous approach and a lighter version in the youtube videos. In parallel we explored other sources for a more practical perspective. The CTfS book is a systematic introduction to Category Theory, with definitions and proofs. I liked that, it is a solid foundation. The examples are mostly from math (Set,

On Defining Messages

“Defining Message Formats” is the title of a message posted on the Service Oriented Architecture mailing list [1]  which attracted a lot of attention.  The post summarizes  the dilemma faced by solution architects when they have to define a service interface: 1. Base the internal message format on the external message standard (MISMO for this industry). This message set is bloated, but is mature, supports most areas of the business, and is extensible for attributes specific to this company and its processes. 2. Create an XML-based message set based on the company's enterprise data model, which the company has invested a great amount of money into, and includes a very high percentage of all attributes needed in the business. In a nutshell, we've generated an XML Schema from ER Studio, and will tinker with that construct types that define the payloads for messages. 3. Use MISMO mainly for its entity definitions, but simplify the structure to improve usability. We benefit from the