UBORA e-platform architecture
The e-platform is currently a single service web application (a monolith) and is fully custom-built in the sense that it does not use any existing project/task management and wiki software underneath, but instead takes advantages of many smaller pieces of free open-source technology.
List of used technologies
For simplifying the construction of a best standards compatible user interface with unified experience, we elected to use Bootstrap 4, a modern HTML/CSS/JS framework, as the foundation. Other than the overall visual feeling, Bootstrap 4 has helped us with many components we need for the UBORA e-infrastructure: notifications, alerts, navigation menus, modals etc.
We use jQuery to provide client-side interactivity for some parts of the e-infrastructure.
Because the UBORA e-infrastructure has many parts for users to input text with links, pictures and videos, which necessitate a WYSIWYG editor for the best user experience, we chose an free open-source editor QuillJS. It uses HTML-s content editable element as input but internally stores it’s own document model.
A simple library for converting Quill’s format to HTML on the back-end using Node.js.
Select2 and autocomplete.js
Node Package Manager (NPM)
Azure App Service
We used Microsoft’s Azure (platform as a service [PaaS]) for hosting the e-infrastructure. Here we have flexible pricing based on usage and we can easily upscale and downscale and use other advanced cloud features.
Azure Blob Storage
For storing all the files uploaded by UBORA users, we use Blob Storage service by Microsoft Azure. Non-public files uploaded to a UBORA project are secured with a shared access signature (SAS token). The secured files are authorized and served through the UBORA e-infrastructure based on the privileges of the user.
Azure Application Insights
We monitor the usage statistics of the platform using the Application Insights service by Microsoft Azure.
In addition to the unit tests on codebase level, we write automated user interface tests. WebdriverIO provides a nice API and it uses Selenium internally.
ASP.NET Core MVC
Modern open-source cross-platform framework for building back-end web services with the C# programming language using model-view-controller pattern.
ASP.NET Identity Core
High quality membership engine for storing users and authenticating users.
Open-source cross-platform polyglot database engine for storing the state of our business entities. We use both the relational and document database capabilities of PostgreSQL.
An open-source abstraction layer over PostgreSQL for .NET which enables safer and more productive development experience. Marten features architectural patterns in a tested and easy to use manner: event-sourcing, multi-tenancy, soft-deletes etc.
The build for the UBORA e-infrastructure is containerized in a Docker image which can be deployed to different environments and provides a single source of truth for a working build. New developers can quickly set up the environment to start developing the e-infrastructure on their computers.
We use Git version control with Bitbucket to put up all the new code to be merged into the platform as pull requests which other developers can code review and test independently. We imagine we will move to GitHub when it comes time to open-source the codebase.
A universal document converter we use to convert HTML to Open Document Format (.odt).
A library for invoking Node.js code from the back-end inside .NET application.
The development supporting continuous integration (CI) server which is the single source of truth whether new versions of the e-infrastructure are buildable & deployable. The main indicator is running all the tests successfully.
UBORA e-infrastructure is built on top of the free open-source cross-platform database engine PostgreSQL. It is a relational database that can effectively store documents using the JSONB type. Thanks to the .NET’s Marten abstraction, we created a document-oriented database, which includes all the good parts of an SQL database (e.g. ACID operations, foreign keys), and is well-fitted with the object-oriented domain model.
Thanks to this choice, we have gained in developer productivity due to reduced trade-offs when modeling data: if an object can be serialized into a JSON type, it can be stored in the database. When a problem is best resolved using relational data, then it can be easily “drop down” to relational model with PostgreSQL and Marten. If some database query is best optimized using relational data and indexes, data can be easily duplicated from the document to the relational table. The technique of combining database storing paradigms is called “polyglot persistence”.
Overview of the .NET solution
This project holds the most important building blocks and internal API of the application: commands, queries, events, entities & specifications. The state of the application is persisted in an immutable append-only event store (event sourcing). The domain entities are a projection of successfully persisted events.
When a new event persists, it is applied to all the relevant inline projections to check for any domain validation errors: if any error is found then the event will not be persisted. If an event is successfully persisted, subscribers can do additional work based on it (for example the platform can send out notifications to one or many of the end-users based on the type of event).
Ubora.Web is the main executable ASP.NET Model-View-Controller (MVC) project which holds the HTTP endpoint controllers, data transfers objects (view & post models), authorization logic and other services.
The UBORA e-infrastructure uses policy-based authorization and resource-based authorization. Each policy has one or more requirements, which can have one or more handlers. Users are authorized on each HTTP request and the system does not blindly trust any data that comes from the client-side, because they can always be tampered.
Ubora.Domain.Tests & Ubora.Web.Tests
These projects hold unit and integration tests for the components that make up the e-infrastructure. The file structures usually correspond to the projects with real implementation. For example, when Ubora.Domain holds UserProfile.cs under the folder Users, the Test project will have UserProfileTests.cs under the same folder. When developing new components, the unit tests have been written before the real implementation, to check if the component works properly and how the API looks to its users. In this way, when the real implementation is created after the test, we will get fast feedback (much faster than starting the e-infrastructure and clicking through) to have a so-called safety net when future developers will modify the component.