Sometimes engineering an application can feel like this: daunting but incredible at the same time.

Domain Pattern in JavaScript Application Engineering

Marc Cyr
8 min readOct 14, 2015

Note: Coming back to this and looking at it months later, and having used Redux for awhile now, I recommend reading this but still moving on to using Redux rather than rolling your own solution. The tools and community around Redux are both fantastic. To fully understand Redux, I recommend implementing it yourself as a simplified version to understand the innards. After doing so, using it in complex applications will be much simpler.

Domain Pattern in JavaScript Application Engineering is a potentially foreign concept to some. It borrows concepts from purely functional languages and applies them to JavaScript. The result is a scalable and extensible architecture that allows for a significant amount of component reusability and an all around stronger application architecture.

A domain is a field of study that defines a set of common requirements, terminology, and functionality for any software program constructed to solve a problem in the area of computer programming. — source: Wikipedia https://en.wikipedia.org/wiki/Domain_(software_engineering)

Another great resource for this idea was linked in the comments by Keith Ensign. Check out more info about Domain-driven design here and you can find Eric Evans’ book on Amazon by clicking here. I came by this pattern through another source, but plan to take a look at that book asap.

Why a domain?

Separation of Logic from UI

The use of a domain to handle logic is optimal because it allows us to separate logic from UI. By building out a logic module and keeping it separate from UI modules, it allows us to utilize the same logic throughout the application while pairing it with different UIs to suit different interactions.

For example, maybe you have a Message Domain and you need to support many different message UIs (think in terms of user messaging, not instant messaging). User Messages will commonly have the same structure throughout an application. They would likely have an id, a message, and a type. The data structure of a message may look something like the following:

{
id: 'bc37a9b5-8581-4e0b-b26b-335677775cca',
message: 'Something went wrong!',
type: 'ERROR'
}

It is logical to assume all user messaging would need to adhere to this structure. Even a success message would have a uuid, a message (“Successful!!”) and a type (“SUCCESS”).

By housing all of the logic in one place, separate from the UI, you maintain complete flexibility in showing messages to users in many different ways. Maybe some would be a banner that comes down from the top of the page, while other messages would render in context of the part of the app in which the user is operating. The world is your oyster in this way.

State can only be changed in one place

The use of this pattern enforces the isolation of mutability. If you’ve worked with React.js (and even if you have not), you may be very informed on this idea. One of the key tenets of React is that you want to isolate mutability as much as possible inside your application, and use immutable objects everywhere else. This way, you know exactly where state is changing so that application maintenance is very possible in huge apps, rather than letting things get out of control (like they would with bi-directional data binding… which is a discussion for another day).

Using this architecture, state can only be changed one way and that is by dispatching an action (yes, this is indeed similar to Flux architecture in this way). This has a couple of key benefits: 1) It reduces complexity in the code by locating logic that causes state to change in one place and 2) it makes maintaining code significantly easier because you know that there is only one path that can be used to update the state of a component.

Isolating change to a single point of mutation is a strategy for achieving a balance between maintaining a state and reducing complexity as much as possible. This pattern controls the scope of change by isolating the thing that can change. Instead of passing an object around and letting anything change its state in place, we put the object (data) inside of a container and force everything to change that instead (via actions). — Matthew Jaquish

What goes into a domain?

The idea of a domain is heavily influenced by functional programming architectures from other languages, but for something close you should check out Redux. The ideas put forth there are similar to and influenced what I am discussing here.

Implementing this in the UI

The best way to approach the UI for this pattern is to build your UI in such a way that it just does not care what it is handed, so long as it is handed all of the “things” that are required for it to render. This is where using React.js can really shine. You would want to build your React components so that they don’t do any logic handling themselves. You hand the React component whatever props it needs and render based on those props. Your UI should not contain any of the logic for the Domain, and as such it will be completely agnostic. This helps to enforce a beneficial pattern that has emerged in React.js. That pattern is one of stateless components. In fact, in the newest version of React (0.14 was just released), there is a new way of writing stateless components. You pass the props as an argument and return the element you want to render. An example from the React docs:

// A functional component using an ES2015 (ES6) arrow function: 
var Aquarium = (props) => {
var fish = getFish(props.species);
return <Tank>{fish}</Tank>;
};
// Or with destructuring and an implicit return, simply:
var Aquarium = ({species}) => (
<Tank>
{getFish(species)}
</Tank>
);
// Then use: <Aquarium species=”rainbowfish” />

Stateless components are great because they get us even closer to high predictability and high “ease of maintenance” of applications (and they might be the epitome of idempotent components in UI).

A pure, stateless React component not only does not care what is in the Domain, you don’t even pass the instance of the Domain to the component. This separation of concerns means that you can use ANY UI on top of the logic and because they are not coupled at all, you could even throw away the UI if you wanted to completely redesign it.

This is the reason why you can use the exact same underlying logic for many different UIs. This can lend itself well to engineering across platforms, especially with the introduction of React Native.

It is entirely possible to build out your domains and use them as the logic backing up an app in the browser, and separate apps for iOS and Android (and potentially native desktop apps as well). The consistency in the logic breeds predictability and extensibility without rewriting code, and the separation of the UI from the logic brings about tremendous flexibility.

Applying this to an existing project:

There are multiple ways to approach applying domain architecture to an existing project. As with anything involving major changes to an application, it is best to take a piecemeal approach and change a small section at a time, much like one does when converting a Backbone.js application to React.js.

Now, I’ll identify three possible approaches for applying this pattern to an existing project. I am actually using a combination of these depending on the particular use case.

One approach to adding a domain to a current application is to create a module that acts as the “glue code” for the domain and the UI. Your domain should have a way to listen for state change and the function for this should execute code for rendering the UI when state changes. You also want the module to export convenience methods as an API for other developers to use on your team. This way, you tuck away all the inner-workings so that people don’t have to know how to do everything themselves to use the module. If another dev does need to drop down into the code it is simple and straightforward for them to understand how things work.

The module itself would likely export a function than can be called, which returns the convenience API methods.

That’s one method for gluing things together so that you can use the domain with the UI. What if you need a user’s interaction with one domain to potentially have an impact on another domain?

Consider this: a user does something in the application that may or may not create the need for us to use the User Messaging system discussed earlier to display a warning or error to the user. If the user’s interaction is successful, you probably just want the user to continue on with what they’re doing. If, however, what the user does creates an error (maybe they entered something wrong), you will likely not want to allow them to continue and instead show them an error message.

Well, to do this we absolutely do not want one domain communicating directly with another domain. This would couple things together in a way that becomes problematic.

One way to solve this is to create an application-level API. The “glue code” for any domain could use the application-level API as needed to interact with other domains.

Another way to solve this would be to create an application-level state (credit due to Matt Jaquish, who came up with this idea and also influenced all the ideas discussed here), which would be an object whose keys would be the domain names, and the values would be the stores of the domains themselves. Then, create an application-level dispatcher and any time an action comes in, you hand it to every domain in the application state.

This works because one of the cores of a domain (which I realize I should have mentioned sooner) is that if the domain does not know what to do with an action, it will just return the current state unchanged. So, if you pass an action to every domain in the application, only the domain that can use it will consume it and return a new state. The others will just return the same state they had before, and the state change listener will not react since there will be no change. This would ensure that every domain that needs to do something with a given action will do it.

Just some things to think about in terms of implementation. Like most things in JavaScript, there are probably a couple more possibilities (if not more) out there for achieving this.

All this is great, and it is fun to talk about the theoretical approach, but by now you probably want to see the code. Unfortunately, I cannot disclose the code itself. I can, however, point to similar projects like Redux. It also would not be too crazy to roll your own solution for this!

Go forth and build applications with consistent logic and flexible UI layers!

Cheers!

--

--

Marc Cyr

Husband and father. Head of UI for Prisma Cloud at Palo Alto Networks running a large engineering team. Angel investor in Silicon Valley. https://20x.capital