In this post, we take a look at a sample application built on top of the modelix stack during my 20% time at itemis. The application allows for real-time collaboration between all clients. It doesn't matter if a client is MPS or the web interface.
Below you find a short demo video about what the application can do and in this post, we dig into some of the details of how it works.
The language engineering tool of choice is MPS. With MPS the language is designed and the MPS-specific editors are created. On top of that, a language-specific model client is generated from MPS. The generated code is a representation of the MPS meta-model but does not require MPS at runtime. The client is entirely independent of MPS and only requires the model-api and the model client from modelix to work. The client also exposes the meta-model as easy-to-use classes. Somebody working with the client doesn’t need to be aware of the fact that language engineering is used nor do they require specific MPS knowhow. For them, it simply looks like classes they can manipulate.
This language-specific model client is used to build a web application. In this example, the application was created using ktor, a Koltin framework for building server applications.
From an MPS perspective, nothing really changes. The language is modeled the same way as usual. Storing models within the modelix model server works seamlessly with the modelix MPS persistence. The modelix persistence supports importing, synchronizing, and exporting models out of the box.
The only additional step in MPS is to export a language-specific modelix model client. Generating the client is done via code generation and requires no change to the language definition. The language-specific model client will include the constraints defined in the structure aspect of MPS but will not contain scopes or other aspects of the MPS language. The client allows for similar operations as the smodel language from MPS but without a dependency on MPS at runtime.
But why not use and connect it directly to the model server? There are a couple of reasons for that:
We don't trust the client/browser. All data we get from the client requires scrutiny. We cannot ensure that the client isn't misbehaving. If we were to connect the browser directly to the model server, the model server would need to check if the edits by the client conform to the meta-model and its constraints. As of now, the modelix model server isn’t concerned with the constraints provided by the meta-model. The server is used to store the model and to distribute changes to the clients. For an untrusted client to connect to the model server we would need to enforce the model constraints on the model server. The server might even need to know the version of the meta-model that is assumed by the client if we want to support meta-model versions and migrations on the server.
The server side is entirely written in Kotlin using the generated model client and ktor. Editors are defined with the kotlinx.html DSL which allows for type-safe templating. Kotlins flexibility enables extending kotlinx.html with simple and reusable templates to define the editors like this:
On top of that, the sample uses TailwindCSS which is a CSS framework adopting a utility CSS approach. It pairs nicely with server-side templating. Providing similar flexibility as inline styles but without many of their limitations.
Client / Browser
The client is mostly boring. It uses Turbo and Stimulus to progressively enhance the user experience. Turbo is used to update the editors within the browser when the model changes. The server will render the editors and push the changes to the client. The client code doesn’t even know about nodes, it replaces parts of the DOM based on ids.
This being a small example built within a couple of days there are lots of parts missing.
While our demo at server.modelix.org does support authentication it isn’t integrated into this example. All edits are technically done by the same user and the web application doesn’t have authentication at all right now. Adding this would be straightforward.
Being a collaborative editor you want to know where somebody else is editing, ideally within MPS and within the web application. For instance by showing a cursor position like Google Docs is doing or by highlighting the input element of the editor where somebody else is editing.
Human to Human Collaboration
With presence, one would know that someone is in the same area but it doesn’t communicate what the other one's intentions are. Ideally, we would be able to pull up a chat window that allows messaging everyone editing the same thing. Or even to start a voice/video call. While we can technically deconflict concurrent edits it is always best to avoid these conflicts by enabling the people involved via communication.
The example doesn’t use any type-system or checking rules defined in MPS and that is for a good reason: right now there is no easy way to reuse them. While having a specific implementation for editors is ok or even desired to be able to provide a web-specific user experience, rewriting complex model checking rules isn’t. Support for reusing MPS type checking and model checking might land in modelix soon though.
If you liked the content consider subscribing to the email newsletter below. The newsletter delivers all posts directly into your inbox. For feedback on the topic feel free to reach out to me. You can find me on Twitter @dumdidum or write a mail to email@example.com.