I've worked with microservice architectures in "embedded backends" as well as in "traditional backends" in the web domain. In my opinion the main difference is the need for and the ability to scale services. In "embedded backends" there is usually no need for scaling services horizontally
(you cannot magically make hardware available in a device) and it's not possible to scale services vertically (the compute and memory resources in a device are fixed). I'd highly recommend to have a look at https://microservices.io/patterns/microservices.html cause it makes it pretty obvious with what complexitiy you have to deal with when developing services in "traditional backends" with vertical scalability requirements (running several service instances concurrently, ensuring reliable communication between them, ensuring business transaction lead to data consistency in error cases). In my opinion the argument that vertically scaling services are more reliable than non-scaling services or modular monoliths is extremly weak. In my experience the opposite was true. In "traditional backends" it makes a lot of sense to apply Domain Driven Design cause business transactions spanning several bounded contexts (services/modular monolith apps) are usual. In case service communication between services in "embedded backends" needs to be reliable and/or data consistency needs to be very good intuitivelly I'd favour remote procudure calls (e.g. gRPC) over messaging bassed busses (using MQTT, ZeroMQ) these days. From experience I can tell that messaging technologies need to be considered with care.
I've used RPC based communication in the web domain with a modular monolith as well. The scalability requirements have not been massive and there was a need for strict data consistency. It worked out pretty well cause the skills and discipline in the team was good.
I think we are talking about two different things: you about the "backend" and me about the "frontend" (see my post Extracting Microservices from a Modular Monolith). Backend means a cloud server like a device management server (e.g., Memfault) or an IoT server for monitoring, coordinating or controlling devices. Frontend means the device itself. Some years ago, we would have called the frontend "client" and the backend "server".
I use microservices on the frontend - on the device. Horizontal scaling - more instances of the same device - doesn't play a role there. Vertical scaling - more RAM, more flash, more powerful CPUs, GPUs and NPUs - is also negligible as at most 5-10 microservices would run on a device. Most SoCs can easily handle this. Hence, horizontal and vertical scaling are not among the reasons for extracting a microservices from a modular monolith.
As the applications and microservices run as separate processes on the same device, I only need inter-process communication. I don't need communication between different nodes in a network. Communication with other nodes (e.g., over CAN, Ethernet, RS485, Bluetooth) is handled in the adapters of the modular monolith or in the extracted microservices. For inter-process communication, my preference is D-Bus, as it is available on every embedded Linux system anyway. With gRPC or ZeroMQ, I would eventually need gateways to D-Bus - meaning extra work.
For me, the most important thing about microservices is their small, high-level and behaviour-driven interface. My experience is that most interfaces in monoliths are on a far too low abstraction level. Every change "behind" the interface ripples through the whole application.
Regarding Memfault: I integrated an SwUpdate client with a Memfault server last year for an OEM. My problem is that neither SwUpdate nor Memfault provide a clean interface (API). I had to cobble together URLs for checking the availability of an update and for downloading an update from the Memfault server. I also had to integrate the application with the SwUpdate client.
I think that the providers of OTA update solutions should provide an API such that an OEM doesn't even know which update client and server they use. If the OEM can replace the OTA update solution from one vendor with that of another vendor without having to change anything in their applications, the API is right. In short, the update solution vendors should provide a microservice for OTA updates with a standardised interface. Of course, they don't want it for obvious reasons. Most of their excuses are just lame.
Great post! Most of what you've stated and what you consolidated I fully agree with.
I'd like to add that Memfault provides OTA capabilities for embedded Linux for some time as well (https://memfault.com/embedded-linux).
I've worked with microservice architectures in "embedded backends" as well as in "traditional backends" in the web domain. In my opinion the main difference is the need for and the ability to scale services. In "embedded backends" there is usually no need for scaling services horizontally
(you cannot magically make hardware available in a device) and it's not possible to scale services vertically (the compute and memory resources in a device are fixed). I'd highly recommend to have a look at https://microservices.io/patterns/microservices.html cause it makes it pretty obvious with what complexitiy you have to deal with when developing services in "traditional backends" with vertical scalability requirements (running several service instances concurrently, ensuring reliable communication between them, ensuring business transaction lead to data consistency in error cases). In my opinion the argument that vertically scaling services are more reliable than non-scaling services or modular monoliths is extremly weak. In my experience the opposite was true. In "traditional backends" it makes a lot of sense to apply Domain Driven Design cause business transactions spanning several bounded contexts (services/modular monolith apps) are usual. In case service communication between services in "embedded backends" needs to be reliable and/or data consistency needs to be very good intuitivelly I'd favour remote procudure calls (e.g. gRPC) over messaging bassed busses (using MQTT, ZeroMQ) these days. From experience I can tell that messaging technologies need to be considered with care.
I've used RPC based communication in the web domain with a modular monolith as well. The scalability requirements have not been massive and there was a need for strict data consistency. It worked out pretty well cause the skills and discipline in the team was good.
Hi Florian,
I think we are talking about two different things: you about the "backend" and me about the "frontend" (see my post Extracting Microservices from a Modular Monolith). Backend means a cloud server like a device management server (e.g., Memfault) or an IoT server for monitoring, coordinating or controlling devices. Frontend means the device itself. Some years ago, we would have called the frontend "client" and the backend "server".
I use microservices on the frontend - on the device. Horizontal scaling - more instances of the same device - doesn't play a role there. Vertical scaling - more RAM, more flash, more powerful CPUs, GPUs and NPUs - is also negligible as at most 5-10 microservices would run on a device. Most SoCs can easily handle this. Hence, horizontal and vertical scaling are not among the reasons for extracting a microservices from a modular monolith.
As the applications and microservices run as separate processes on the same device, I only need inter-process communication. I don't need communication between different nodes in a network. Communication with other nodes (e.g., over CAN, Ethernet, RS485, Bluetooth) is handled in the adapters of the modular monolith or in the extracted microservices. For inter-process communication, my preference is D-Bus, as it is available on every embedded Linux system anyway. With gRPC or ZeroMQ, I would eventually need gateways to D-Bus - meaning extra work.
For me, the most important thing about microservices is their small, high-level and behaviour-driven interface. My experience is that most interfaces in monoliths are on a far too low abstraction level. Every change "behind" the interface ripples through the whole application.
Regarding Memfault: I integrated an SwUpdate client with a Memfault server last year for an OEM. My problem is that neither SwUpdate nor Memfault provide a clean interface (API). I had to cobble together URLs for checking the availability of an update and for downloading an update from the Memfault server. I also had to integrate the application with the SwUpdate client.
I think that the providers of OTA update solutions should provide an API such that an OEM doesn't even know which update client and server they use. If the OEM can replace the OTA update solution from one vendor with that of another vendor without having to change anything in their applications, the API is right. In short, the update solution vendors should provide a microservice for OTA updates with a standardised interface. Of course, they don't want it for obvious reasons. Most of their excuses are just lame.
Cheers,
Burkhard