In the previous essay, we talked about the product vision of Odoo. In this essay, we will elaborate on the architecture of the project and specifically the HR module.
Odoo consists of a three-tier architecture1. This architecture divides the application into the following tiers (see figure below):
- The presentation tier, consisting of a user interface (UI) on the web
- The application tier, where logic of the application resides
- The data tier, where data is stored and managed.
The database tier stores all information and configurations of the Odoo modules. Meanwhile, on the application tier resides the Odoo server. This server stores application logic, handles communication between different modules, and provides the Object Relational Mapping (ORM) tool to transfer data from the Odoo server in Python to a PostgreSQL database server2. The highest tier, which is written in Javascript, is the presentation tier (UI in a web browser).
Furthermore, Odoo employs the Model-View-Controller (MVC) pattern. This pattern is applied to keep data and UI separated. This way, changes to the data schema do not impact the UI and vice-versa. A controller connects the two components and acts as a mediator handling the logic and data transformations.
Within this pattern, some communication between components takes place to run the system smoothly. The model communicates with the view, to notify that data has been modified. That way the view can redraw content. Additionally, the view has limited access to the controller to interact with the system. However, this dependency should be minimal as the controller, as well as the view, should be replaceable. This pattern applies to all modules within Odoo. As was mentioned in the previous blog post, the Odoo suite exists of multiple decoupled modules that allow for extensibility and modularity.
Execution environments
Odoo provides some freedom with regards to deploying the system. The system could be deployed on a couple of systems, using only a local database, as well as on a cluster of machines, where even issues such as load balancing should be taken into account. Even in these large settings where availability is key, the system could either be hosted on-premise (the infrastructure of the company) or on the cloud3. An example of a deployment architecture is shown in the figure below, where a multitude of servers hosts the Odoo suite. Every request done is handled by the load balancer, which distributes the requests. Meanwhile, all servers retrieve and write information using locks on the same database, the PostgreSQL server. PostgreSQL also handles consistent reads and writes using multi-version control, which handles on the basis of recent snapshots of the database4.
Component view
Within the rest of the blog, we will focus on the Odoo Human Resources toolkit, as HR is applicable to many companies. The toolkit includes the following apps: Employees, Recruitment, Fleet, Expenses, Time Off, Attendances, Skills Management and Lunch.
Odoo HR solutions are divided not only into separate applications but also into separate addons. If an app is installed from the admin panel, one or more addons are added to the running code. The main addon is hr, which is required by all other addons and is installed with Odoo Employees. The hr addon introduces main entities such as employee or department to which other addons refer. Additionally, all addons use features from the core modules, for example by registering an endpoint to handle their own requests or to acquire logged in user.
The database schema also reveals the dependencies, where entities introduced by nested addons relate to entities from parent add-ons. The figure below shows how hr_employee_skills (addon hr_skills) refers to hr_employee (addon hr).
As it’s common in Python and Javascript systems, these relations aren’t clearly depicted in the source code. Indicators that these addons relate to each other are mostly through naming conventions, but it’s easy to violate such a convention (for example the _employee_category_rel table, which doesn’t begin with _hr_). Also, all addons are located in a single folder creating a flat directory structure. This makes finding code harder and hides dependencies.
Connectors view
Odoo’s architecture is layered, which creates the necessity of communication between layers. However, this layering is two-dimensional:
- Web application layers: frontend, backend and database.
- Modular application layers: core, apps, extensions.
Typical web application architecture
The first dimension is a well-established standard and it contains two connections. The first connection, from the frontend to the backend, is more advanced than usual because it’s bidirectional. This is required by some features (e.g. chat, notifications) that need to react both to user actions and to events that arise on the server-side. Odoo implements this using long-polling5 created in JsonRPC6. The second connection, between the backend and the PostgreSQL database, is performed using the package Psycopg27.
Innovative extensibility architecture
The second dimension is much more complex. We already showed that the entities from higher layers will refer with keys to entities from the lower layers. That is the connector in the database layer. Note that these layers are open - entities can refer to ids in every lower layer, not only the one directly below it. For example almost all entities refer to the user entity from the core layer with an audit field like _create_uid _and _write_uid_.
In the backend layer, there are two types of connectors: class inheritance and model inheritance. Class inheritance allows entities from addons to inherit the functionalities of the classes from the core. This is a good way to for example access the database in a uniform way. Model inheritance is Odoo’s mechanism to access entities defined in other addons. This way addons can extend already existing models in the system. With these two connections, two reference trees are created that are hard to follow, especially in the case of the custom model inheritance, which isn’t supported by the IDE and the user has to resort to searching by the string.
In the frontend layer, addons are rendered inside each other forming another kind of dependency. The figure below shows the skills feature rendered inside the employee’s card. However, there are also cases where dependent apps are rendered separately from each other (e.g. Employees and Recruitment).
Development view
The development view describes the architecture that supports the software development process and focuses on the concerns that are architecturally significant for this process.
Module organization
Odoo has an enormous codebase with 100K+ commits and more than a thousand contributors. To be able to manage dependencies and allow developers to effectively work on them, this codebase is structured into modules that consist of addons. Since Odoo offers a suite of applications, it contains hundreds of addons. Every application has a certain amount of addons that depend on each other in different ways. An overview of the HR module has already been given.
Common processing
Since Odoo has such a large system, having separate code modules to isolate common processes is beneficial. For this, Odoo has separate addons along with a tools-and Odoo directory. These directories contain files that provide common functionalities, for example database access or authentication.
Standardization of design
Standardizing aspects of the design increases maintainability, reliability and technical cohesion. Since every application in Odoo’s software suite is different, it can be hard to standardize design. To overcome this, Odoo has given every add-on a standardized internal structure. This structure can be found in the figure below.
Standardization of testing
To ensure a consistent and efficient testing process, testing approaches, technologies and conventions can be standardized. They do this by using the QUnit framework for three different kinds of tests8:
- Python unit tests
- JavaScript unit tests
- Integration tests, called Tours
They also employ Odoo Runbot9, which automatically runs tests and deploys specific branches.
Instrumentation
Odoo provides a set of tools that can trace during runtime the execution of methods and perform additional logs10. Using them, developers can gain useful knowledge that can help support them when finding bugs or tackling other problems related to the system.
Runtime view
As mentioned before, the architecture of the system consists of three tiers. At runtime, the presentation tier communicates with the application tier and the application tier communicates with the data tier. The runtime view is displayed in the figure below.
Users communicate with the server through their (web-)client. This communication is conducted using XML-RPC or JSON-RPC (previously NET-RPC). The server contains the business applications (as modules). This is where all the logic happens. To communicate with the data tier, queries can be executed from the business objects. However, most of the access to the data tier is done through ORM.
Realization of Architecture Key Quality Attributes
Within this section we discuss how the presented architecture of the Odoo HR toolkit realizes key quality attributes and any potential trade-offs that have been made within them. The reason for the selected attributes was justified in the previous essay.
Usability
Firstly, Odoo is completely web-based and requires no installation or special prerequisites to use. As a result, the toolkit can be used on any device improving accessibility and usability (as the system can be accessed on the move). Secondly, one important architectural decision made to realize usability is to re-use interface views across apps in all the toolkit. This has a positive effect on the codebase, as code can be reused, and results in an interface that is familiar across all apps.
Customizability
Each app in the toolkit comes with a set of settings that can be customized in the admin panel. These settings are somewhat fixed but nonetheless realize some level of customizability. Introducing the Enterprise edition is another way by which the architecture of the toolkit realizes customizability. It allows additional customizability and includes more features. It was a deliberate architectural decision to not open-source the Enterprise edition, because Odoo is not just an open-source project but also a business. This shows mindfulness about the business-side of Odoo of software architects.
Modularity
As has been mentioned before, the toolkit consists of many apps. Businesses can pick and choose which apps suit their scenario best. In particular, these apps are described as modules (or add-ons) in the codebase. This reflects how the architecture realized modularity, as each app which serves a single use case is a module that can be added or removed and also is completely separate from other modules.
Correctness
To ensure correctness the toolkit has both a back-end (mainly Python) and a front-end (mainly JavaScript) which are both tested individually, but also together using integration testing. Browserstack11 is employed to execute integration testing across many browsers to ensure correctness. Finally, thanks to continuous integration any change made to the codebase results in all tests being automatically executed before merging.
API Design Principles
The toolkit itself doesn’t really provide an API but instead employs a large more general API which is referred to as the Module API. In particular, each app in the toolkit is a module that employees the Module API. The Module API is also key in facilitating communication between the back-end and the front-end. The Module API allows apps in the toolkit to expose methods which can then be called by the front-end.
Within the Module API we notice that the explicit interface principle is maintained. Methods exposed are explicitly documented, and don’t seem to be communicating using undocumented methods. The principle of least surprise seems not to be maintained as the Module API has a steep learning curve and doesn’t always quite work as you would expect it to work. The principle of uniform access is maintained as methods and properties of the API can be assessed in the same way. The few interfaces principles are somewhat maintained, but there are still a lot of methods. Finally, the clear interface principle is maintained as methods are well-documented and have clear names.
Conclusion
In summary Odoo has an interesting architecture focused around modularity to implement the quality attributes important to them.
-
IBM Education. (2020, October 28). Three-Tier Architecture. IBM. https://www.ibm.com/cloud/learn/three-tier-architecture ↩︎
-
Odoo. (n.d.-b). Architecture. Odoo Developer Documentation. https://doc.odoo.com/trunk/server/02_architecture ↩︎
-
Odoo. (2014, June 10). Improving the performance of Odoo deployments. Slideshare. https://www.slideshare.net/openobject/performance2014-35689113 ↩︎
-
Chapter 13. Concurrency Control. (2021). PostgreSQL Documentation. https://www.postgresql.org/docs/current/mvcc.html ↩︎
-
Internet Engineering Task Force. (2011, April). RFC 6202 - Known Issues and Best Practices for the Use of Long Polling and Streaming in Bidirectional HTTP. IETF Tools. https://tools.ietf.org/html/rfc6202 ↩︎
-
JSON-RPC. (n.d.). JSON-RPC. https://www.jsonrpc.org/ ↩︎
-
psycopg. (n.d.). PostgreSQL driver for Python — Psycopg. https://www.psycopg.org/ ↩︎
-
Odoo. (n.d.-c). Testing Odoo — odoo 14.0 documentation. https://www.odoo.com/documentation/14.0/reference/testing.html ↩︎
-
Odoo. (n.d.-c). Runbot R&D. Odoo Runbot. https://runbot.odoo.com/ ↩︎
-
Odoo. (n.d.-c). Profiling Odoo code — odoo 14.0 documentation. https://www.odoo.com/documentation/14.0/howtos/profilecode.html ↩︎
-
BrowserStack. (n.d.). Most Reliable App & Cross Browser Testing Platform. https://www.browserstack.com/ ↩︎