It’s impossible to ignore the topic of microservices today. We hear about them from social media, conferences, specialized websites, books, framework vendors, communities, colleagues. It seems like they’re the only way to go.
But are they? To answer this question, we first need to explore why are microservices so useful, what are their downsides and what are the alternatives.
Using Conway’s Law for fast development
organizations which design systems … are constrained to produce designs which are copies of the communication structures of these organizations — Melvin E. Conway
Conway’s Law has been first stated in 1967, and it has been proven empirically again and again. To understand it, let’s discuss a few examples of its application:
Teams who can communicate easily (typically small and collocated) tend to create monoliths
Teams who communicate through intermediaries (managers, architects etc.) tend to work on separate modules
Teams who communicate through hierarchies tend to work on complex structures. For example one core / framework team, front-end teams, back-end teams etc.
Fred George started one of the first microservices projects with a big challenge: the work had to be organized in a way that allowed adding developers fast. He decided to use Conway’s Law to their advantage, and create an architectural structure that allowed adding pairs of programmers to the development. The only option was to create many small services that communicated asynchronously and that were completely independent of each other, each developed by a pair of programmers. Enter microservices.
A few rules for microservices
According to Fred George, they followed very specific rules for the architecture of the microservices. They started with a long session of domain modelling (about two weeks), that resulted into bounded contexts* and then into well-defined services. Each service:
uses its own database
depends only on a message pipeline, never directly on another service
when needing something from another service, posts a message to the pipeline describing the need
periodically reads the messages from the pipeline and decide if anything needs to be done
self-monitors: if something doesn’t work, report and try to self-heal
is deployed separately
when changed, a new service instance is deployed. Once the callers switched to the new instance, the old instance can be deleted
In addition, code duplication between services is ignored. Otherwise, libraries will be extracted that increase deployment complexity and can lead to dependency hell.
These constraints allow for independent development by a lot of programmer pairs. Architecture and Conway’s Law work hand in hand, helping the development go faster.
Why are microservices useful?
In conclusion, microservices are useful because of two things:
scale up the team very quickly by taking advantage of Conway’s Law
modular development
In addition, microservices can scale very easily and minimize spending on the cloud. The reason is that when load increases, more specific instances can be started at a minimal cost.
As we can see from the industry examples, these criteria apply very well for large projects that need to scale quickly. So if you need to start such a project, by all means, use microservices. But if you don’t need to scale that quickly, there are easier ways. The reason is that microservices, like any architectural approach, are a trade-off.
A downside and a huge mistake
Microservices have one important downside and can lead to one huge architecture mistake.
The downside is that microservices architecture leads to huge complexity increases for deployment and operations. Deploying one monolith is very different from deploying 100 or 1000 separate services. Monitoring and de-bugging then become a very difficult job. There are solutions, but each adds to costs and complexity. This investment might not pay o for smaller products.
The architecture mistake is to create microservices that directly depend on other microservices. This is just a re-creation of the dependency hell problem in deployment. If dependencies are hierarchical, changing a service propagates and require a lot of redeployments. Instead, aim for services that completely encapsulate one atomic change. This is the hardest thing to do about microservices; no wonder it took Fred George’s team weeks to figure it out (and probably a lot more time after starting the development).
Modular monolith
There is an alternative to microservices, one that we used successfully for eventriX: the modular monolith.
The idea of a modular monolith is to preserve the idea of encapsulation from above but deploy it in a different manner. We don’t have to deploy a module as a service until we need to scale it. Instead, modules can be deployed as libraries, plugins, namespaces etc., resulting into one file that can be very easily deployed, monitored and debugged. As long as we follow a good modular structure inside the monolith, we can easily extract a service.
So how can we structure such a monolith? It obviously depends on the application. In the case of eventriX, we follow a few clear rules:
there are three large bounded contexts: speaker, event organizer, and reviewer. More will appear in time.
each of the bounded contexts has its own URL mapping: “/s” for speaker, “/o” for organizer, “/r” for reviewer
the bounded contexts don’t depend on each other. When changing some- thing for speaker, nothing changes for organizer. This means that some views, although containing the same information, are duplicated between speaker and organizer. That’s acceptable because speaker views can change with a different speed and in a different direction than organizer views
each request goes through a clear set of classes: a controller, a command object that validates the request, one or more queries, one or more application service etc. It’s very easy to follow the path of each request, and the paths are different for each request
All these rules allow us to quickly extract separate services or plugins if needed while maintaining the operational complexity at a minimum.
Conclusion
We have found microservices to be a great idea for large projects that need to scale quickly and save money when the load increases. But they also add huge operational complexity. We’ve also seen that the most complex part of developing microservices is getting the modularity right. But teams who master modular architecture have more than one option.
A modular monolith or a well-thought combination between plugins, modular monoliths and libraries allow fast development while maintaining low complexity. They also allow evolving to a more appropriate architecture when the need appear since logical modules can easily be extracted into physical modules.
The decision between microservices and modular monolith is not easy. It requires careful consideration of the trade-offs and of the context. It’s up to the person(s) playing the role of the architect to end the best way. We hope this article helps you reach a decision.
2 thoughts on “Modular Monolith Or Microservices?”
jason
Where are the downsides of the modular monolith approach?
Alexandru Bolboaca
The typical downside is that it requires discipline from developers. It’s very easy to call classes and functions that are in another namespace; it’s more difficult to maintain the discipline of calling only the entry points to the modules inside a monolith.
I’ve seen however how code review policies and checklists can solve this issue, with a lot less cost than the operational overhead of microservices.
This is not to say that microservices are not good. It is however important to understand where they fit in the architectural landscape.
Comments are closed.
Manage Consent
To provide the best experiences, we use technologies like cookies to store and/or access device information. Consenting to these technologies will allow us to process data such as browsing behavior or unique IDs on this site. Not consenting or withdrawing consent, may adversely affect certain features and functions.
Functional
Always active
The technical storage or access is strictly necessary for the legitimate purpose of enabling the use of a specific service explicitly requested by the subscriber or user, or for the sole purpose of carrying out the transmission of a communication over an electronic communications network.
Preferences
The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.
Statistics
The technical storage or access that is used exclusively for statistical purposes.The technical storage or access that is used exclusively for anonymous statistical purposes. Without a subpoena, voluntary compliance on the part of your Internet Service Provider, or additional records from a third party, information stored or retrieved for this purpose alone cannot usually be used to identify you.
Marketing
The technical storage or access is required to create user profiles to send advertising, or to track the user on a website or across several websites for similar marketing purposes.
var astra = {"break_point":"921","isRtl":"","is_scroll_to_id":"1","is_scroll_to_top":"1","is_header_footer_builder_active":"1","responsive_cart_click":"flyout","is_dark_palette":"","revealEffectEnable":"","ajax_url":"https://mozaicworks.com/wp-admin/admin-ajax.php","infinite_count":"2","infinite_total":"0","pagination":"infinite","infinite_scroll_event":"scroll","no_more_post_message":"No more posts to show.","grid_layout":{"desktop":"3","tablet":1,"mobile":1},"site_url":"https://mozaicworks.com","blogArchiveTitleLayout":"","blogArchiveTitleOn":"","show_comments":"Show Comments","enableHistoryPushState":"1","masonryEnabled":"","blogMasonryBreakPoint":"0","shop_infinite_count":"2","shop_infinite_total":"0","shop_total_products":"1","shop_pagination":"number","shop_infinite_scroll_event":"scroll","shop_no_more_post_message":"No more products to show.","shop_result_count_format":"Showing 1\u2013{count} of {total} results","checkout_prev_text":"Back to my details","checkout_next_text":"Proceed to payment","shop_quick_view_enable":"disabled","shop_quick_view_stick_cart":"","shop_quick_view_auto_height":"1","woo_cart_empty_featured_product":"","single_product_qty_ajax_nonce":"e93debc8af","quick_view_nonce":"849fc62228","single_product_ajax_add_to_cart":"1","single_product_ajax_url":"/?wc-ajax=astra_add_cart_single_product","is_cart":"","is_single_product":"","view_cart":"View cart","cart_url":"https://mozaicworks.com/cart","checkout_url":"https://mozaicworks.com/checkout","add_to_cart_options_single":"rt_add_to_cart","is_astra_pro":"1","shopRevealEffectEnable":""};
//# sourceURL=astra-theme-js-js-extra
document.addEventListener( 'wpcf7submit', e => turnstile.reset() );
//# sourceURL=cloudflare-turnstile-js-after
var astraAddon = {"is_elementor_active":"","sticky_active":"1","svgIconClose":"\u003Cspan class=\"ast-icon icon-close\"\u003E\u003C/span\u003E","hf_account_show_menu_on":"hover","hf_account_action_type":"link","hf_account_logout_action":"link","header_main_stick":"1","header_above_stick":"","header_below_stick":"","stick_header_meta":"","header_main_stick_meta":"","header_above_stick_meta":"","header_below_stick_meta":"","sticky_header_on_devices":"both","sticky_header_style":"none","sticky_hide_on_scroll":"1","break_point":"921","tablet_break_point":"768","mobile_break_point":"544","header_main_shrink":"1","header_animation_effect":"none","header_logo_width":"","responsive_header_logo_width":{"desktop":200,"tablet":"","mobile":150},"stick_origin_position":"","site_layout":"ast-full-width-layout","site_content_width":"1070","site_layout_padded_width":"1200","site_layout_box_width":"1024","header_builder_active":"1","component_limit":"10","single_product_sticky_product_image":"","single_product_sticky_product_image_top_offset":"0","cart_sticky_cart_totals":"","order_review_toggle_texts":{"toggle_show_text":"Show Order Summary","toggle_hide_text":"Hide Order Summary"},"check_user_exist_nonce":"cbcdd699d6","woocommerce_login_nonce":"e740847d90","is_logged_in":"","user_validation_msgs":{"error_msg":"Entered user detail is not a valid.","success_msg":"This user is already registered. Please enter the password to continue.","login_failed_msg":"Login failed. Please reload the page and try again."},"checkout_order_review_sticky":"","cartflows_version":"","is_registration_required":"1","is_complete_package":"1","is_header_builder_active":"1"};
//# sourceURL=astra-addon-js-js-extra
var xoo_wsc_params = {"adminurl":"https://mozaicworks.com/wp-admin/admin-ajax.php","wc_ajax_url":"/?wc-ajax=%%endpoint%%","qtyUpdateDelay":"500","notificationTime":"5000","html":{"successNotice":"\u003Cul class=\"xoo-wsc-notices\"\u003E\u003Cli class=\"xoo-wsc-notice-success\"\u003E\u003Cspan class=\"xoo-wsc-icon-check_circle\"\u003E\u003C/span\u003E%s%\u003C/li\u003E\u003C/ul\u003E","errorNotice":"\u003Cul class=\"xoo-wsc-notices\"\u003E\u003Cli class=\"xoo-wsc-notice-error\"\u003E\u003Cspan class=\"xoo-wsc-icon-cross\"\u003E\u003C/span\u003E%s%\u003C/li\u003E\u003C/ul\u003E"},"strings":{"maxQtyError":"Only %s% in stock","stepQtyError":"Quantity can only be purchased in multiple of %s%","calculateCheckout":"Please use checkout form to calculate shipping","couponEmpty":"Please enter promo code"},"nonces":{"update_shipping_method_nonce":"d738eabd81"},"isCheckout":"","isCart":"","sliderAutoClose":"1","shippingEnabled":"","couponsEnabled":"1","autoOpenCart":"yes","addedToCart":"","ajaxAddToCart":"yes","showBasket":"always_show","flyToCart":"yes","flyToCartTime":"1500","productFlyClass":"","refreshCart":"no","fetchDelay":"200"};
//# sourceURL=xoo-wsc-main-js-js-extra
var complianz = {"prefix":"cmplz_","debug":"","user_banner_id":"1","set_cookies":[],"block_ajax_content":"0","banner_version":"22","version":"7.5.6","store_consent":"","do_not_track_enabled":"1","consenttype":"optin","region":"eu","geoip":"","dismiss_timeout":"","disable_cookiebanner":"","soft_cookiewall":"","dismiss_on_scroll":"","cookie_expiry":"365","url":"https://mozaicworks.com/wp-json/complianz/v1/","locale":"lang=en&locale=en_US","set_cookies_on_root":"0","cookie_domain":"","current_policy_id":"26","cookie_path":"/","categories":{"statistics":"statistics","marketing":"marketing"},"tcf_active":"","placeholdertext":"Click to accept {category} cookies and enable this content","css_file":"https://mozaicworks.com/wp-content/uploads/complianz/css/banner-{banner_id}-{type}.css?v=22","page_links":{"eu":{"cookie-statement":{"title":"Cookie Policy ","url":"https://mozaicworks.com/cookie-policy-eu"},"privacy-statement":{"title":"Terms And Conditions","url":"https://mozaicworks.com/terms-and-conditions"},"impressum":{"title":"Private: Contact Old","url":"https://mozaicworks.com/?page_id=12523"}},"us":{"impressum":{"title":"Private: Contact Old","url":"https://mozaicworks.com/?page_id=12523"}},"uk":{"impressum":{"title":"Private: Contact Old","url":"https://mozaicworks.com/?page_id=12523"}},"ca":{"impressum":{"title":"Private: Contact Old","url":"https://mozaicworks.com/?page_id=12523"}},"au":{"impressum":{"title":"Private: Contact Old","url":"https://mozaicworks.com/?page_id=12523"}},"za":{"impressum":{"title":"Private: Contact Old","url":"https://mozaicworks.com/?page_id=12523"}},"br":{"impressum":{"title":"Private: Contact Old","url":"https://mozaicworks.com/?page_id=12523"}}},"tm_categories":"","forceEnableStats":"","preview":"","clean_cookies":"","aria_label":"Click to accept {category} cookies and enable this content","iapi_nav":"fallback","is_cookiepolicy_page":""};
//# sourceURL=cmplz-cookiebanner-js-extra
Where are the downsides of the modular monolith approach?
The typical downside is that it requires discipline from developers. It’s very easy to call classes and functions that are in another namespace; it’s more difficult to maintain the discipline of calling only the entry points to the modules inside a monolith.
I’ve seen however how code review policies and checklists can solve this issue, with a lot less cost than the operational overhead of microservices.
This is not to say that microservices are not good. It is however important to understand where they fit in the architectural landscape.