DSC01227

The technology behind Sulu 2.0

Thomas has already explained our intentions for the next major release of Sulu. He mentioned that we have decided to use React as the main technology for our new administration interface, because we think it is a very powerful view-layer, quite popular for some time now and the team working on React lives certain values like keeping backwards compatibility, which matters a lot to us.

However, since React only covers the view-layer of our application I will also explain in this article which other technologies we are using and the reasons to do so.

React

At first the most critical choice. For us React was the way to go because it is backed by a huge company and also seems to have great adoption outside of it. There are multiple conferences attended by over a 1000 people each year which shows the enormous potential of the ecosystem.

The only downer from the beginning was JSX. I was very skeptical, since splitting HTML, CSS and JavaScript was the best practice for many years. However, after working a bit with it I am now the biggest fan. Instead of separating by technologies we are able to separate by concerns and colocate HTML, CSS and JavaScript in a way that makes a lot more sense in my opinion. And this weird HTML/XML syntax within JavaScript doesn't bother you after a very short amount of time. In fact it turns into the exact opposite and you cannot imagine going back.

Another popular option would have been VueJS but it misses a really big backing organisation and I don't really like its syntax. I think it makes much more sense to reuse my JavaScript knowledge to loop through arrays instead of having to learn a separate syntax like in VueJS.

Redux or MobX?

I think one of the most important questions when starting to use React is how to handle state. While React is great at rendering different states its internal setState mechanism has some drawbacks. It seems like a pretty easy concept but it still seems to need pretty long articles in order to explain it properly. And it binds the state to a concrete component which means it is not that easy to have a single source of truth. I have also tried to use the internal setState but after a few minutes I already ran into problems like explained in this blog post.

Apart from that it was obvious to us that we would like to have a single source of truth. That's exactly what Redux offers. It has an architecture that reminds me a bit of CQRS which also comes with the same downsides. It is quite hard to work with because for almost everything you do you need to define an action, an action creator and a reducer. So for implementing even simple features you need to touch at least three different files which feels a bit like an overkill to us. The other thing is that even the creator of Redux seems to think that it is overused.

The other state management solution we were aware of and finally favoured over the others is MobX. What I like about it so much is that it has just the right amount of magic. It allows you to define some observable values and let some code automatically run as soon as these values change. That's really nice when being combined with React. The observables are the single source of truth and all of your React components dependant on that value automatically rerender if necessary. There are a few gotchas but overall it still feels like a great choice.

We also can't support the statement that MobX only works for smaller projects. We would consider Sulu 2.0 quite a big project and that's exactly where MobX excels. For smaller codebases it might work to update stuff manually but with larger code bases Mobx really shines because it takes care of everything being dependant on an observable.

Another reason to go for MobX was that it feels easier to extend for different resources and it is closer to what PHP developer are used to. And since our driving force is still the PHP community that is a very important criteria.

CSS Modules

I think everybody has seen this one CSS codebase from which everybody is afraid to delete some code because there is no way to find out if the specific statement is being used in some weird situation. That's one of the reasons we decided to use CSS modules. It allows us to give CSS classes a local scope so that the definitions from a certain CSS file only have a very limited scope. It does that by adding a hash to the class names. This way it is impossible to break e.g. the navigation component only because we've adjusted something in the button component (at least as long as we are not using the :global selector).

It also allows us to colocate the CSS and JavaScript for a specific component in one place. So it is very easy to find the styles for a given component.

There is also the new CSS-in-JS hype which does not only take the HTML into JavaScript but also the CSS. Styled Components is probably one of the first and one of the most popular CSS-in-JS libraries. They allow you to use JavaScript functions within styles which is a very powerful technique. However, we still decided to go with CSS modules because to us it seems easier to write dependent rules with hover styles and so on. Apart from that styled-component was not extracting the styles to a separate CSS file when we started which would not have been optimal for performance.

The building blocks of our architecture

Sulu 2.0 Architecture Sulu 2.0 Architecture

Apart from the previously mentioned tools we are also using babel and webpack to translate and bundle modern ES6 code but that appears to be pretty the standard these days. What's more important is how we have built our architecture.

We are defining 5 different type of classes in Javascript. Each of these types have a separate folder in our folder structure.

Components are simple building blocks and have only internal state. They don't take any external state except for props and can be easily reused. You can think about a table, an input field, a date-picker or similar stuff. Components are React components and are only allowed to use other components.

Stores are JavaScript classes containing more complicated state. This state can also be passed around in order to be reused in multiple places. This way it is possible to reuse that state in different containers. Containers are other React components that are allowed to retrieve external state via stores. For instance our new Datagrid will share its DatagridStore with our Toolbar container. This way it is possible to select something in the datagrid and automatically enable the correct buttons in the toolbar for the selected elements.

Containers are also allowed to use services which are classes not being dependant on React. Examples for theses classes are the router, which allows us to recognize the value of the URL and act accordingly, or some classes for creating requests.

Then we've got the most interesting building block - Views. We have reserved the most popular spot in our administration interface for views. They are React components being rendered in this spot based on the current URL. We have developed a few standard views like the Datagrid and a form which can be reused without manual coding any JavaScript. That's possible because the routes for the React application are actually defined on the server side using PHP. You can also pass some options to these routes so that they can be easily reused under different circumstances. At the same time the views are also the most interesting extension point for you because you can build a React component within where you are free to implement anything that's necessary.

However, just giving some options to each view is often not enough for the complex information you need to pass to a form or a datagrid. Therefore we also introduced the concept of ResourceMetadata. You can use XML to define different aspects of your entities, similar to what you are already used to for defining templates. This metadata will then be converted to JSON, transferred to the browser and can then be reused to correctly render and validate forms as well as send requests to the server.

Conclusion

I am really excited about Sulu 2.0! Not only because we are switching to a completely new frontend stack but also because it will make it a lot easier to implement custom entities. The biggest pain in Sulu 1.x is to implement lists and forms for the frontend part of the administration interface. With the new architecture it willl be possible to avoid implementing JavaScript, as long as all you need is a standard list and a form consisting only of standard components. And even if you need some special fields you can register your own field type and use it within the standard form instead of implementing a complete form on your own. There are a lot more interesting extension points and I can't wait to share them with you!