Modular Monoliths - with Steve “ardalis” Smith


Summary

In this episode, Dan Clark is joined by Steve “ardalis” Smith to explore the concept of modular monoliths. Steve explains that a modular monolith is a single deployable application composed of independent, encapsulated modules that communicate through well-defined contracts, similar to microservices but without the overhead of distributed systems.

The discussion begins by defining what a module is in .NET terms—typically a collection of projects (the module itself, a contracts project, and tests) that enforce boundaries and prevent circular dependencies. Steve emphasizes that modularity provides the benefits of microservices (independent development, clear boundaries, scalability) while maintaining the simplicity and performance of a monolith.

Steve introduces a two-axis framework for architectural decisions: the horizontal axis represents the number of deployables (from one monolith to many microservices), and the vertical axis represents modularity (from no boundaries to high modularity). He argues that many teams should consider moving “up” the modularity axis within a monolith before moving “right” to microservices, as distributed systems introduce significant complexity.

The conversation covers practical implementation details, including using MediatR for in-process messaging, separate databases per module, and leveraging .NET’s project system and InternalsVisibleTo for encapsulation. Steve also discusses cross-cutting concerns like validation, logging, and tracing via MediatR behaviors, and tools like NetArchTest for enforcing architectural rules.

Finally, Steve shares his developer tip: when maintaining legacy code, prefer creating new classes and methods over modifying existing ones, as this gradually improves testability and structure. He also mentions his courses on Dometrain and the modular monolith template available on GitHub.


Recommendations

Articles

  • Martin Fowler’s article on monoliths and microservices — Referenced by Steve, this article advises building a monolith first before considering microservices, warning of the dangers of starting with microservices prematurely.

Courses

  • Steve Smith’s Modular Monolith courses on Dometrain — Steve mentions his courses on Dometrain that teach how to build modular monoliths in .NET, including one focused on moving from microservices back to a modular monolith.

People

  • Nick Chapsas — Mentioned by Dan as a previous guest who talked about FastEndpoints, which Dan saw used in Steve’s course.

Tools

  • MediatR — A library used for in-process messaging with commands, queries, and events. Steve uses it heavily in his modular monolith implementation to facilitate communication between modules via their contracts.
  • NetArchTest (or similar architectural test libraries) — Tools that allow you to write unit tests to enforce architectural rules, such as preventing dependencies between specific namespaces or projects.
  • Steve’s modular monolith template (moduleth) — A GitHub template Steve created to help developers quickly set up a modular monolith structure in .NET.

Topic Timeline

  • 00:00:00Introduction and guest background — Dan Clark introduces the episode and guest Steve “ardalis” Smith. Steve gives a brief introduction about himself, his company Nimble Pros, and his focus on teaching developers to write maintainable, high-quality code using design patterns, Domain-Driven Design, and clean architecture.
  • 00:02:17Defining modules in .NET and encapsulation — Steve explains what a module is in the context of a .NET modular monolith. He describes it as a collection of projects that enforce boundaries and encapsulation, preventing unauthorized access to data and logic. He emphasizes the importance of encapsulation for maintaining system invariants and avoiding the “big ball of mud” problem.
  • 00:05:11When to choose a modular monolith over microservices — Steve discusses when teams should consider a modular monolith. He introduces a two-axis framework: number of deployables (monolith vs. microservices) and level of modularity. He suggests that many teams should first add modularity to their monolith (move up the vertical axis) before considering microservices (move right), as distributed systems add significant complexity.
  • 00:11:14Hybrid approaches and independent scalability — The conversation explores hybrid architectures where a modular monolith can have specific modules extracted as microservices if needed for independent scalability. Steve references Martin Fowler’s advice to build a monolith first, understand the system, and then extract modules as microservices only when necessary, keeping most of the system efficient and in-process.
  • 00:17:19Preventing unwanted coupling and enforcing boundaries — Dan asks how to prevent developers from accidentally introducing coupling between modules. Steve highlights code reviews and pair programming as key practices. He details his technical approach using three projects per module (module, contracts, tests) and .NET project references to enforce dependencies only through contracts, preventing circular references.
  • 00:23:44In-process messaging vs. distributed messaging — The discussion compares using MediatR for in-process messaging (like notifications/events) versus using external message brokers (like Azure Service Bus). Steve argues that for in-process communication, the durability and complexity of a message broker are usually unnecessary, as the risk of failure is the same as any method call. Durable messaging is more appropriate for cross-service communication.
  • 00:33:00Cross-cutting concerns with MediatR behaviors — Steve explains the power of MediatR behaviors for handling cross-cutting concerns like validation, logging, tracing, authentication, and caching. He describes how these behaviors create a middleware pipeline similar to ASP.NET Core, allowing developers to keep business logic clean and apply consistent patterns across all handlers automatically.
  • 00:36:26Module-level encapsulation and architectural enforcement — Steve elaborates on using modules as a larger encapsulation boundary than individual classes. By marking module internals as internal and using InternalsVisibleTo for tests, teams can enforce architectural rules. He also mentions tools like NetArchTest for writing unit tests that enforce dependency rules, providing another layer of protection against boundary violations.
  • 00:40:20Origin and popularity of the term ‘modular monolith’ — Dan asks about the origin of the term “modular monolith.” Steve notes it’s been around for several years but has gained popularity as teams experience “microservice hell” and seek an alternative that avoids both distributed complexity and the “big ball of mud.” He warns against the “distributed monolith”—microservices that are still tightly coupled, sharing databases and lacking true boundaries.
  • 00:41:51Developer tip: Prefer new classes for maintenance — Steve shares his developer tip: when doing maintenance programming, default to writing new code in new classes or methods rather than modifying existing, potentially messy code. This approach makes the new code unit-testable, allows for better design, and gradually improves the overall codebase structure. It also facilitates the use of feature flags.

Episode Info

  • Podcast: The Unhandled Exception Podcast
  • Author: Dan Clarke (@dracan)
  • Category: Technology
  • Published: 2024-07-09T17:00:00Z
  • Duration: 00:44:50

References


Podcast Info


Transcript

[00:00:00] hey everyone welcome to the unhandled exception podcast i’m dan clark and this is episode number

[00:00:22] 68 and today i’m joined by steve smith also known as r dallas on the socials so welcome to the show

[00:00:29] steve hi thanks for having me you’re welcome i know i’m kind of i’ve got a feeling that you

[00:00:34] probably need no introductions because you’re quite well known in our community but for those

[00:00:38] that haven’t heard of you could you give the listeners a quick intro into yourself and what

[00:00:41] you do sure my name is steve smith that turns out to be a super common name so i’ve been going by

[00:00:46] r dallas online for a couple decades now because usually i can get that username and i work for

[00:00:52] nimble pros which is a company my wife and i run and we have a small consultancy where we help

[00:00:58] teams build better socials

[00:00:59] software faster and one of my passions for the last i don’t know 20 years has been

[00:01:03] mentoring and training developers on how to write code that’s more maintainable and higher quality

[00:01:08] and so things like design patterns domain driven design clean architecture testability all that

[00:01:14] stuff are near and dear to my heart and things i enjoy sharing with with others i know you mean

[00:01:19] about names because dan clark is also a very popular name so that’s just i couldn’t believe

[00:01:23] i managed to get the domain name danclark.com this was a long time ago i grabbed it and then

[00:01:29] i got it from someone that was a racing car driver called dan clark who wanted it so i said you can

[00:01:33] have it for x thousands of pounds or whatever and they quickly said no that’s all right and i can see

[00:01:37] they’ve got something else now so i’ve kept that all along there was a time when when i was in the

[00:01:43] first page of google results for steve smith but that that time has long since passed so um before

[00:01:50] we jump into much of monoliths just a quick reminder to everyone that this podcast does

[00:01:54] have a discord community and if you head over to the website unhandledexceptionpodcast.com

[00:01:59] there’s a big discord link there and also a thank you to everstack for sponsoring the podcast

[00:02:03] everstack’s my own company providing software development and consultation services for more

[00:02:07] information visit everstack.com and speaking of sponsors the show is currently looking for

[00:02:12] additional sponsors if you’re interested the details are on the website right so module

[00:02:17] monoliths so a small confession i really wanted to watch all of your course before we did the

[00:02:23] recording and i got about 50% of the way through but time you know what it’s like yeah but so i’ve

[00:02:29] i’ve seen half of it but um it might actually be a good thing that i’ve not listened to the full

[00:02:33] course because i guess it means my questions aren’t seeded by like knowing questions and it

[00:02:39] can be a bit more of an organic conversation so this podcast is a .net podcast and your course is

[00:02:45] it’s module monoliths but it is for .net so to help listeners visualize this a bit better can we

[00:02:50] first start off with just talking about from a code point of view from .net what a module is in

[00:02:55] terms of this .net stuff because i think when i’ve looked at this initially

[00:02:59] i’ve heard lots of abstract stuff but i wanted to visualize this as a .net developer so is a

[00:03:04] module is that literally just a .net project like an assembly it can be and that’s mostly the the

[00:03:11] approach that i take in this course there’s a few related classes and and there’s some reasons for

[00:03:17] that the thing about c-sharp is that it doesn’t support friend assemblies and so there’s no way

[00:03:29] there’s no one concept that you can663 probably connected with certain other projects in order to

[00:03:35] allow for modularity but also allow for encapsulation and i’m a big fan of if you’re

[00:03:41] doing object oriented programming of using encapsulation so that you can enforce invariants

[00:03:45] and you know have a contract that you maintain with your collaborators with your clients

[00:03:50] then allows you to kind of change the inner workings of your class uh however you want

[00:03:55] but more importantly to make sure that other things can’t go in and just fiddle with the data

[00:03:59] real big reasons why people ought to be thinking about building more modular systems, whether

[00:04:04] monoliths or microservices, is because if you don’t, if you just have a typical application

[00:04:10] with one database, and maybe many applications all sharing a database, that’s more common,

[00:04:15] at least in the enterprise, you have no control over who can touch any of your data.

[00:04:20] Yeah, because any application has access to that database can insert or update records anytime they

[00:04:24] want. And that makes it really difficult for you to, to make any kind of guarantee about the state

[00:04:29] of your system. I’ve worked, and I’m sure we all have with lots of legacy code bases, where over

[00:04:36] time, lots of developers have touched this and different design patterns have used. And as you’re

[00:04:41] saying, different things are talking to different parts of the system, different databases, all the

[00:04:46] same database, even and treading on each other’s toes. And just when you look at almost like a kind

[00:04:52] of end depends diagram, right?

[00:04:54] And you’ve got spaghetti lines of code everywhere and stuff. It does feel like, I guess, if you were

[00:04:59] starting from scratch and not doing microservices, would you ever build a monolith that isn’t

[00:05:05] modular? Is this mainly about improving legacy balls of wood monolith where it’s got to this

[00:05:10] state?

[00:05:11] Yeah, I don’t know that I would necessarily start with a modular monolith every time it would depend

[00:05:16] on what I expected the scope of the application to be. So this would be, you know, an architectural

[00:05:20] decision. You could just build a and most applications,

[00:05:24] should just be built as simply as possible with with a single project, and get some stuff working and

[00:05:30] get some fast feedback from stakeholders. But as it continues to grow, it’s going to slow down, because

[00:05:36] of the fact that it’s going to get more and more complicated. And every new feature you add has to

[00:05:40] interact with all the existing features. And it’s at that point that you need to start having some more

[00:05:45] discipline and some more thought into how can I make sure that when I do add additional features, I’m

[00:05:52] not breaking the things that are already there.

[00:05:54] And that’s what tends to happen.

[00:05:54] And that’s what tends to slow companies down and codebases down is where you used to be able to get a feature done in a day. And now

[00:06:01] you’re like, maybe we can get a feature done in a month. But we’re also gonna have to spend, you know, two weeks fixing bugs

[00:06:06] everywhere every time we try and do that, because everything is a tightly coupled big ball of mud.

[00:06:12] Yes, another one. Well, it starts off so fast with the Greenfield project development and knocking features out left, right and

[00:06:17] center, then over time, it becomes Yeah, just like wading through a big ball of mud, really, I guess, right, a very good

[00:06:24] term. You mentioned about databases before. So like, obviously, if you’re doing when doing microservices, it tends to be

[00:06:31] recommended where like each service or what’s the DDT term,

[00:06:37] each bounded context,

[00:06:38] family context as it was after. So each boundary context has its own database and things. And with modular monoliths, is it the same

[00:06:45] concept that each module would have its own database?

[00:06:48] Yes, you can think of each module in a modular monolith as being equivalent to a microservice. And in terms of all the rules that you would

[00:06:54] apply to a microservice, typically, in terms of design, like the fact that they should be independent from one another, they should be

[00:07:01] able to be deployed independently, they shouldn’t share global states through a database or some other mechanism. All those rules

[00:07:07] apply to modules in this modular system. That’s what makes them modular. If they, if they did have tight coupling to other modules,

[00:07:15] then they wouldn’t be modular, right? Imagine if you had Lego bricks, but you glued them together, like you no longer have two Lego

[00:07:21] bricks, right? You have one bigger Lego brick. So

[00:07:23] that’s, that’s important.

[00:07:25] To to keeping that separation for for this to work.

[00:07:28] Yeah, it definitely does feel like way more productive doing this work. So now, I’ve worked with a couple of systems that are more quotes

[00:07:35] microservice kind of architectures. And that were, if it’s all in one solution, one one solution, it can be pretty much a function call,

[00:07:43] which is super easy, you can debug into it, you can do all this stuff, where when it’s always HTTP or gRPC calls, and you’ve got to

[00:07:51] manage the, like the actual details.

[00:07:54] And if you debug, you can’t just step into it. And that kind of thing feels way more productive. It feels like there’s so many pros and cons to using

[00:08:04] microservices versus like a modular monolith. It feels quite hard for a team to decide is, have you got some good questions that a team should ask

[00:08:14] themselves? Or is this kind of just a module monolith is a good stepping stone to actually start with that. And then if you need to go to

[00:08:22] microservices, you can then break it down further.

[00:08:24] Yeah, there’s a diagram that a lot of people have used, and I use it in my modular monolith course. And think of it with two axes, right? So horizontally on the x-axis, there is the number of things you deploy for your application, the number of, you know, independent services, let’s say, right? And so on the left, you’ve got one, which would be a monolith. And on the right, you’ve got many, right, however many. And so microservices would be, you know, in the right column, let’s say.

[00:08:51] But now vertically, we’ve got two options as well.

[00:08:54] We’ve got zero modularity, where everything can touch anything else, and we haven’t put in place any kind of boundaries, like, you know, when we talk about a bounded context, it has boundaries around it.

[00:09:05] And so if you have no boundaries, if you have no modularity, then you’re at the bottom of the vertical axis.

[00:09:10] And if you have modularity, if you have boundaries, then you’re higher up, right?

[00:09:14] And so every application starts in the bottom left, right?

[00:09:18] You do file new project, and it says hello world.

[00:09:21] You have no modularity.

[00:09:23] You just have one project.

[00:09:24] You have one deployable, and that’s where you stay.

[00:09:27] And you may stay there for a long time, right?

[00:09:29] Many years, you might build a huge system there.

[00:09:32] But if you get a really large, complex system, then it does tend to turn into a big ball of mud because it doesn’t have that modularity.

[00:09:38] And so many developers, especially in the last 10 or 15 years, have seen that to be a problem.

[00:09:43] And the industry has told them the solution, the salvation, is to go to microservices.

[00:09:48] Because that is the only way that you could decouple all your logic and have them be bounded.

[00:09:55] And in this case, the way we’re going to make them separate is because we’re going to put a network between these two things.

[00:10:00] So the only way they can talk to each other is over some out-of-process communication channel.

[00:10:05] And so you’re moving diagonally, right?

[00:10:06] You’re moving to the right by deploying more services, and you’re moving vertically because you’re achieving modularity.

[00:10:12] And it turns out that that’s a really big step.

[00:10:14] That’s a really big learning curve for a lot of teams.

[00:10:16] Because understanding architect.

[00:10:18] Building and maintaining a distributed architecture like that is leaps and bounds more difficult than a monolith.

[00:10:25] And so, yes, my proposal for many teams would be, you know, unless you have a really good reason to go to a microservice architecture,

[00:10:32] it makes more sense to only move across one axis, move up, and add modularity to your monolithic application so that now everything is still in one process.

[00:10:42] You can still build it and run it and debug it in one solution.

[00:10:45] And when you deploy it, it’s one thing.

[00:10:47] But you still have all the benefits of modularity.

[00:10:48] Which means that you can develop these different modules with separate teams if you want.

[00:10:55] A lot of folks use microservices as a way to scale up the number of developers.

[00:10:59] They can throw out the problem.

[00:11:00] Well, you can do that with modules in a monolith just as easily as you can with microservices.

[00:11:05] And all the other benefits you get from that modularity without having to deal with all the fallacies of distributed computing that come along with microservices.

[00:11:12] Makes total sense.

[00:11:14] And I guess presumably you can do a hybrid as well.

[00:11:16] So if you’ve got one module.

[00:11:18] But really has to be scaled out separately for whatever reason, then you can keep the whole system as a module monolith.

[00:11:24] But just pull that one thing out because you’ve broken everything down nicely in the first place into these modules.

[00:11:30] Yeah, exactly.

[00:11:31] Martin Fowler has a nice article from, I don’t know, 10 years ago talking about how to go to microservices.

[00:11:37] And basically the article says, build a monolith first, right?

[00:11:40] If you try and just build microservices up front, here there be dragons.

[00:11:44] And he’s got a nice little picture with dragons on it.

[00:11:46] So, you know, the better thing is to build a monolith first.

[00:11:48] And if you try and just build microservices up front, here there be dragons. And he’s got a nice little picture with dragons on it.

[00:11:48] Build that monolith, understand the system, build it in a modular fashion.

[00:11:51] And then when you say, like, oh, the video transcoder thing needs to scale independently from everything else.

[00:11:57] And it’s its own module, right?

[00:11:58] Well, just take that module and put it into a microservice.

[00:12:01] And great.

[00:12:01] Everything else is still as efficient as it possibly can be in terms of developer productivity and performance.

[00:12:07] Because when everything’s in process, performance is much better.

[00:12:10] Your hosting costs are lower when everything is deployed in a single thing.

[00:12:13] But those individual things that make sense as microservices could certainly still be pulled out.

[00:12:17] Exactly.

[00:12:18] How does this work with, like, front-ends?

[00:12:21] So, like I say, I’m halfway through your course, so I saw that you were using APIs as an example.

[00:12:25] And as an aside, it was quite nice to actually see fast endpoints in action.

[00:12:30] Because we’ve had, when was it?

[00:12:31] I don’t know what episode it was, but Nick Chapsass has been on a couple of times.

[00:12:34] The first time he mentioned this.

[00:12:36] And it’s been, do you know how every developer’s got a long list of things they want to play with?

[00:12:40] But it’s always a long list and it gets bigger.

[00:12:43] It’s been on my list for quite a while, so it was quite good to see that in action.

[00:12:46] But anyway, I kind of diverged from what I was initially saying.

[00:12:48] Yeah, so is it okay to have, like, your entire back-end being kind of a modular monolith,

[00:12:54] but then multiple front-ends if you’ve got different applications that need to use that logic?

[00:12:59] You could have, if you’re building a bunch of APIs, like in my course,

[00:13:03] you could certainly have multiple different clients of that.

[00:13:05] If you’re building a single application, then you can still use a modular monolith approach for the back-end.

[00:13:11] And then depending on what your technology is for your UI,

[00:13:15] there’s various different ways to be more modular in your UI.

[00:13:18] So if you’re using Angular or React, they each have different ways of using components and routes and things like that

[00:13:23] so that you could have different sections of the app that correspond to, I don’t know,

[00:13:27] this is the order processing section and this is the report section or what have you.

[00:13:31] And so you can build modular user interfaces just like you can build modular back-ends.

[00:13:36] And if you are sticking strictly to .NET, you know, Blazor has support for this with Blazor, Razor libraries.

[00:13:42] And if you’re even just using MVC, I mean, you can use areas, you can use Razor pages, you can use controllers and views.

[00:13:48] And those could…

[00:13:48] They could live in separate projects, but all still get pulled together at runtime

[00:13:51] and hosted as a single monolithic ASP.NET Core application,

[00:13:56] even though all their pieces, all the UI components would live in separate modules

[00:14:00] and perhaps be owned by separate teams.

[00:14:03] We’ve spoken about scalability.

[00:14:05] And to be honest, I very rarely see an issue where I need to scale out one particular thing

[00:14:10] because I’m not building Twitter or XNow or Stack Overflow or anything.

[00:14:15] Even like Stack Overflow, I think that they did a post,

[00:14:18] a few years ago, that they showed that running everything literally on one server.

[00:14:23] So it’s kind of, with their load, if they don’t have to do that,

[00:14:26] nothing the average developer is building probably has to as well, very often anyway.

[00:14:31] But one thing I do like about microservices is the independent deployability side,

[00:14:36] where if there’s quite a few developers working in different teams on a system

[00:14:39] and say I need to make a change to an email service, for example,

[00:14:45] which is off the top of my head, then I can quickly make that change.

[00:14:48] And know that I’m not redeploying everything.

[00:14:51] And I don’t know whether that’s actually an issue, redeploying everything,

[00:14:55] or whether it’s just a mental kind of barrier, I guess,

[00:14:58] where it feels a big thing, redeploying everything.

[00:15:02] It is true that when you have a monolith and you deploy any piece of it,

[00:15:05] you have to deploy the whole thing.

[00:15:06] So, I mean, that is a trade off.

[00:15:08] And there’s pros and cons to that.

[00:15:10] Like when you deploy the monolith, you know that if you have a good CI CD pipeline,

[00:15:15] you know that that thing that you just did in your module

[00:15:17] is being integrated with everything else.

[00:15:20] And all the tests are running for all of it, you know, before it gets to production,

[00:15:23] assuming that you’re running it through the CI pipeline first.

[00:15:26] Whereas it’s much more difficult to guarantee that when you’re a team

[00:15:30] that just runs the email sending microservice

[00:15:33] and you want to make a small update and you send it up.

[00:15:36] Like it’s quite unlikely that you have a bunch of integration tests in that case

[00:15:40] that make sure that you are going to operate with every live version

[00:15:44] of every microservice and application that’s out there currently.

[00:15:47] Some teams do that, but it’s more rare.

[00:15:50] And if you do do that, then it adds a much bigger hurdle for you to go over

[00:15:53] every time you want to deploy.

[00:15:54] So it’s all trade offs.

[00:15:57] But I encourage teams to use feature toggles and to deploy as frequently as possible.

[00:16:02] So, you know, you should be deploying your modular monolith

[00:16:05] at least several times a week, if not, you know, daily.

[00:16:08] So then it’s not a big deal to deploy like, yeah, well, I mean,

[00:16:12] we just deployed a couple hours ago. Everything looks good.

[00:16:14] We’re going to deploy again now. OK, everything still looks good.

[00:16:16] That’s why you have a CI CD pipeline.

[00:16:17] Is to ensure that things are all working together

[00:16:21] so that you have confidence when you deploy it, that things will still work.

[00:16:25] And if they don’t, because you just deployed and it worked a couple hours ago

[00:16:29] or yesterday, like you only have a very small amount of things

[00:16:32] to look for for where the problem is, where teams get into trouble

[00:16:34] with these monoliths is when they don’t deploy for weeks or months.

[00:16:39] And they then they finally do and all heck breaks loose

[00:16:42] and they’re trying to figure out, well, what broke this?

[00:16:45] And they’ve got weeks worth of work for multiple developers.

[00:16:47] So try and track down where the problem could be.

[00:16:50] I can definitely echo that, which I’ve seen that a few times,

[00:16:52] even with more microservice architectures, not just monoliths,

[00:16:55] where if for whatever reason some stuff hasn’t been deployed for a while,

[00:17:00] then that’s where the issues come when deploying to prod or actually.

[00:17:04] And as you say, if this if you’ve only got like almost like one commits

[00:17:08] worth of changes and it goes wrong, it’s really obvious

[00:17:11] what broke that thing most of the time anyway.

[00:17:13] Right. And if you need to roll back, I mean, it’s usually pretty easy to roll back

[00:17:16] if it’s just one commit.

[00:17:18] Yeah, definitely. Definitely.

[00:17:19] So I guess one thing I’m aware of with this, like if we’ve got

[00:17:23] one single code base and you’ve got it nicely separated out into modules

[00:17:27] and then you’ve got different developers of different experience levels

[00:17:31] working on that code base,

[00:17:33] because I can picture it being quite easy for a developer to come in

[00:17:36] and just without realizing it, add coupling where you wouldn’t want it.

[00:17:40] Are there techniques to help prevent that?

[00:17:42] Yeah, I mean, code reviews are always going to be your friend there.

[00:17:45] And pair programming can be a form of

[00:17:47] constant review.

[00:17:48] So, you know, some level of discipline in the team to avoid mistakes

[00:17:52] or folks doing things that they shouldn’t do or inadvertently do

[00:17:56] in terms of the structure of the solution itself.

[00:17:59] There’s not like one way to do this.

[00:18:01] So I’ve got my approach that I share in the Dometrain course.

[00:18:05] And what I do is I leverage the project system in dot net heavily,

[00:18:09] and I make sure that the project references only allow the things

[00:18:13] that should talk to each other to do so.

[00:18:15] And what that means is that each one of.

[00:18:17] The modules is a small collection of projects, right?

[00:18:21] Usually three.

[00:18:22] And so there’s a module project that is the thing.

[00:18:25] Like, let’s say it’s an email sending module, right?

[00:18:28] There’s an email sender CS project file.

[00:18:30] That’s that’s the project that has the actual logic, right?

[00:18:34] But now other things need to be able to communicate

[00:18:36] that other modules would like to send email.

[00:18:38] How are they going to do that?

[00:18:39] Well, if they want to call a method that lives on a class inside the email

[00:18:43] sending project, the only way they can do that is to add a project reference to it.

[00:18:47] And.

[00:18:47] If they do that now, you know, they’re coupled to it, you know,

[00:18:50] literally because of that project reference.

[00:18:53] But also that makes it so that there’s no way for the email sending module

[00:18:56] to then depend back on one of them

[00:18:59] because you can’t have a circular reference in the project system.

[00:19:02] So to break that, what I’ve done is I’ve added a email sender

[00:19:06] contracts project, and that is essentially the public interface for this module.

[00:19:12] And so it depends on the actual email sending module and nothing else really does.

[00:19:17] With the exception of its tests.

[00:19:19] So that’s the third project.

[00:19:20] So each module basically has three projects, the module itself, the contracts, and then the tests,

[00:19:25] the unit tests for the module.

[00:19:27] And so now anything else can depend on the contract of another module, right?

[00:19:31] And you’ll never get a circular dependency that way.

[00:19:33] If you have modules A, B, and C, right?

[00:19:36] A can depend on B and C’s contracts, and B and C can depend on, you know,

[00:19:40] the other two’s contracts, and that will never form a cycle.

[00:19:43] And so that’s how you avoid the circular dependency problem, and that’s how you enforce, you know,

[00:19:47] that the different modules only depend on each other through this public contracts project.

[00:19:53] So does that allow you, I’m trying to think of example services,

[00:19:58] a user service and I don’t know, an order service or something,

[00:20:03] but you’ve got two different modules and they need to talk to each other.

[00:20:07] Would you do it via these contracts?

[00:20:09] Yes. And the contracts typically are using mediator.

[00:20:14] So I’m defining commands and handlers and DTS.

[00:20:17] And then I’m defining, you know, what are the different types of DTOs that are in those contracts.

[00:20:22] So, you know, imagine that the contract is almost like your swagger or open API document for an API, right?

[00:20:29] It defines what the different types of messages are that you can use and what the return types are that you’ll get.

[00:20:36] And then in my example, I’m making heavy use of mediator, but there’s other techniques that you could use

[00:20:41] if you didn’t like mediator for whatever reason.

[00:20:43] But, you know, if the order module wants to talk to the customer module, and maybe get customer information,

[00:20:47] it can do that.

[00:20:48] So, you know, the contracts project has a customer details query, let’s say, and also a customer DTO.

[00:20:54] And so it can, the order module can just tell mediator, hey, I want to send a query with this information,

[00:21:01] you know, customer ID 123 and, you know, go send that, right?

[00:21:04] And the handler for that lives in the customer module, right?

[00:21:08] So it will get that command, that query, and it will do whatever it needs to do to get the data.

[00:21:12] It’ll make a call to its database and map it to whatever.

[00:21:15] And it’ll send back a customer DTO.

[00:21:17] And the order module has that customer DTO.

[00:21:19] And that’s very similar to what you might expect to happen in a microservices application.

[00:21:24] But the difference is that in microservices, you would have had to have the order microservice make an HTTP or GRPC synchronous get call

[00:21:32] to the customer microservice over, you know, the network, wait for that to come back, and then continue on.

[00:21:38] And that presupposes that that other microservice is up and running and everything’s working, right?

[00:21:43] And so your brittleness of your system goes up dramatically when you do that.

[00:21:47] Because if your uptime for any given microservice is like five nines, well, if they need to talk to each other,

[00:21:53] those nines start dropping rapidly because they all have to be up at the same time.

[00:21:57] In the modular monolith, it’s all in process, right?

[00:22:00] Even though I’m using mediator, like, you can still step through it with a debugger.

[00:22:03] It’s still, like, happening in nanoseconds, not, you know, over the network hop time.

[00:22:08] Yeah, that makes total sense.

[00:22:10] And did you just say that if people aren’t a fan of mediator, who’s not a fan of mediator?

[00:22:13] I don’t know what you mean.

[00:22:15] There’ll be half the listeners screaming right now.

[00:22:17] And half not listening.

[00:22:19] Yeah, there’s folks out there that think mediator’s slow, and there’s ways you can do it faster.

[00:22:23] You could probably use source generation and get a faster way to do it.

[00:22:26] And there’s, you know, some argument that mediator kind of gives you too much access to everything in a system, right?

[00:22:32] So it’s hard to logically say, when I send some command to mediator, where does that go?

[00:22:37] What are my dependencies, right?

[00:22:39] So there’s a case to be made for mediator being a little bit too broad of an implementation for what it does.

[00:22:44] But it is super convenient.

[00:22:46] It makes, you know, structures like what I’m doing in this course really simple to do.

[00:22:50] So I’m still a fan.

[00:22:52] Yeah, me too.

[00:22:53] I was saying that in jest.

[00:22:54] I know some, as you say, there’s like a love to hate relationship with it.

[00:22:57] But I’m a big fan, to be honest.

[00:22:59] I mean, it’s not automapper that most people hate.

[00:23:01] And even Jimmy hates automapper.

[00:23:03] So, you know, but mediator still has a lot of love.

[00:23:07] It’s funny.

[00:23:08] I won’t go into detail, but like just yesterday in one of my clients’ Teams channels, there was conversations about automapper.

[00:23:15] So it’s quite coincidental.

[00:23:16] But I won’t go into detail, but it’s just quite funny.

[00:23:19] Speaking of mediator, it’s kind of, so if you’re using mediator and mediator’s got this concept of like sending out requests.

[00:23:26] So like using commands and queries and things.

[00:23:28] It’s also got a concept of notifications, which is almost, you can almost, even though it’s a function call, you can almost think about it as pub sub.

[00:23:36] Which sometimes in my smaller projects, I use that for, you know, almost to simulate messaging, even though it is.

[00:23:43] Like conceptually, it feels like.

[00:23:44] You kind of, you can fire an event, something’s happened and you can have a few things like handlers or teams or whatever.

[00:23:51] It’s great for that.

[00:23:52] In a module monolith, would you also reach, because obviously that is a function call.

[00:23:57] So you don’t get the whole, like if you’re using a proper message broker, like Azure Service Bus or Rabbit or whatever you want to use.

[00:24:03] Where you get a bit more resiliency because things can go down and the messages stay in the message broker.

[00:24:09] Right.

[00:24:10] Would you still use like a proper message broker when dealing with a module monolith?

[00:24:13] I don’t think it’s worth doing that personally.

[00:24:15] Because the monolith, right?

[00:24:16] Imagine it wasn’t modular and you weren’t using message passing and you just had a module, right?

[00:24:18] And it made function calls.

[00:24:19] And it goes down.

[00:24:20] Like that happens.

[00:24:21] You know, you know, monoliths go down.

[00:24:22] Right.

[00:24:23] And nobody is saying like, hey, every function call that you do inside your monolith should be going to persistent storage and mass transit and Rabbit MQ.

[00:24:24] So that if, if by chance on this line of code, the system craps out that we will still have that message that ready to go.

[00:24:25] Right.

[00:24:26] Yeah.

[00:24:27] Yeah.

[00:24:28] Yeah.

[00:24:29] Yeah.

[00:24:30] Yeah.

[00:24:31] Yeah.

[00:24:32] Yeah.

[00:24:33] Yeah.

[00:24:34] Yeah.

[00:24:35] Yeah.

[00:24:36] Yeah.

[00:24:37] Yeah.

[00:24:38] Yeah.

[00:24:39] Yeah.

[00:24:40] Yeah.

[00:24:41] Yeah.

[00:24:42] Yeah.

[00:24:43] Yeah.

[00:24:44] Yeah.

[00:24:45] Yeah.

[00:24:46] It will go when it comes back up.

[00:24:47] Like that’s not a thing.

[00:24:48] So, like, you know, we already work with applications where at any moment, on any line of code of execution, the system could crash.

[00:24:53] And mediator is working at that level.

[00:24:55] Right.

[00:24:56] It’s just function calls.

[00:24:57] So, you know, I don’t feel the need when you’re working in process with just pure function calls to try and persist all of that somewhere along the way on the off chance that the app might crash at that moment.

[00:25:06] It is a slight risk.

[00:25:08] But it’s the same risk you have now in any of your applications that you’re using method calls.

[00:25:12] So, you know, we already accept that risk.

[00:25:14] As you say, it’s a trade-off.

[00:25:16] It’s kind of, if you do go down that line,

[00:25:17] then there’s a lot more complexity

[00:25:19] and it just takes more time.

[00:25:21] So it’s kind of, is it worth all that time

[00:25:23] and complexity and ongoing maintenance

[00:25:25] versus just doing it like KISS, keep it simple.

[00:25:29] I don’t like the actual KISS acronym.

[00:25:32] So I always say keep it super simple

[00:25:34] because that still stands for,

[00:25:35] that’s still KISS, isn’t it?

[00:25:36] Right, right.

[00:25:36] I think that’s a nice phrase.

[00:25:37] Keep it simple, silly.

[00:25:38] Yeah, exactly.

[00:25:40] If you don’t want to call people stupid,

[00:25:41] you can still use the acronym, yeah.

[00:25:43] Where I want to use messaging with durability

[00:25:47] is as soon as you have network calls involved, right?

[00:25:50] That as soon as you say,

[00:25:51] I’m not communicating in process,

[00:25:52] I’m talking to a microservice

[00:25:53] or I’m talking to some other system,

[00:25:55] then that’s where I think it makes sense

[00:25:57] to use mass transit or in-service bus,

[00:26:00] RabbitMQ, Azure Service Bus, whatever.

[00:26:02] Because at that point,

[00:26:02] you do have all of the distributed computing issues

[00:26:05] and the likelihood that something will fall down

[00:26:07] in between the sender and the receiver

[00:26:09] is orders of magnitude greater.

[00:26:11] I guess the scenarios I’m thinking about

[00:26:13] is kind of where an action requires something

[00:26:16] on the back end to do something

[00:26:17] that does heavy resource usage,

[00:26:20] maybe CPU or memory or something,

[00:26:21] where actually if you can just shove it on a message broker

[00:26:24] and let something else deal with it asynchronously,

[00:26:27] I think that’s quite nice.

[00:26:28] But yeah, again, if you’ve got that case,

[00:26:30] it’s kind of that particular case, split that off.

[00:26:32] But most of the stuff,

[00:26:34] just because you’ve got like one case that does that

[00:26:36] doesn’t mean you’ve got to make the entire architecture,

[00:26:39] a microservice kind of architecture.

[00:26:40] That’s right.

[00:26:41] And you can implement that without using messaging, right?

[00:26:44] So in my course, I’ve got an email sending module.

[00:26:47] And email sending is certainly something

[00:26:49] that doesn’t need to happen

[00:26:50] while the HTTP request is waiting, right?

[00:26:53] That can be queued up and then sent.

[00:26:55] And so the email module happens to use MongoDB

[00:26:58] just to show that you don’t have to use

[00:26:59] the same database for everything.

[00:27:01] And as soon as it gets a message to send an email,

[00:27:04] it has an outbox pattern that it uses.

[00:27:05] And it just writes a record to MongoDB.

[00:27:08] And then there’s a background worker

[00:27:10] inside that.

[00:27:11] That module that is watching that table

[00:27:13] and just picks things up off the outbox

[00:27:14] and actually sends the message.

[00:27:16] But it’s a separate process.

[00:27:17] It’s still part of the monolith, right?

[00:27:18] But not part of that request thread.

[00:27:20] So you do get that benefit of being able to send it separately.

[00:27:23] And you get that durability where, you know,

[00:27:25] as long as the record was written to the outbox,

[00:27:28] if the whole system, you know, dies at that moment,

[00:27:30] when it comes back up, it’ll still watch the outbox,

[00:27:32] pick it up and send the email.

[00:27:34] That’s a good point.

[00:27:35] With ASP.NET, you can have background workers.

[00:27:37] So you can, it’s not just about those requests coming in.

[00:27:40] And you can

[00:27:41] do various things.

[00:27:41] And what’s the, what’s the cron job library

[00:27:45] that’s quite popular?

[00:27:46] Hangfire.

[00:27:48] There’s Hangfire.

[00:27:49] And there’s another one is Quartz Quartz.

[00:27:50] Maybe it’s, I think it’s Hangfire.

[00:27:51] I’m thinking of, but you can use stuff like that.

[00:27:53] So, but it can all be together.

[00:27:55] So yeah, yeah, I did actually, when was it?

[00:27:58] It was a few days ago.

[00:27:59] I listened to your, cause you appeared on .NET rocks

[00:28:02] talking about the same thing near the end.

[00:28:04] I can’t remember who I think it was.

[00:28:06] Carla brought it up was that you’ve got a course upcoming

[00:28:09] or it depends when I don’t know if it’s out yet or not.

[00:28:11] Right.

[00:28:11] About the reverse going from microservices back to modular monoliths.

[00:28:16] That’s right.

[00:28:17] Yeah.

[00:28:17] That course got published the end of May.

[00:28:19] So of 2024.

[00:28:20] So it’s a, it’s live now and it’s, it’s less technical.

[00:28:23] It’s more for like a technical decision maker, not so much code and more about

[00:28:26] these are all the things you need to consider and how to, how to pull things

[00:28:30] back from microservices into your, your monolith and in a bunch of case studies

[00:28:34] to show you like, you’re not crazy.

[00:28:36] Other companies that you’ve heard of like Amazon and Twilio have published case studies.

[00:28:41] About why they went from microservices back to monoliths in order to save money and

[00:28:45] improve performance and a host of other things.

[00:28:47] So, you know, that’s what the, that course is all about is kind of giving you

[00:28:50] permission to, to reconsider the, the fact that everybody is saying microservices

[00:28:55] are the only way and like, no, are they though?

[00:28:58] I like that term, give you permission.

[00:28:59] Cause it’s, it is almost like you’ve done a lot of work to go to microservices

[00:29:03] and I can picture people feeling that they’re almost stuck because they’ve

[00:29:07] made that decision and commitment.

[00:29:08] And then it’s interesting that you’ve, yeah, you’ve done a course to go.

[00:29:11] Yeah.

[00:29:11] The other way, going back to a modular monolith is that on, is that again on

[00:29:15] dome train?

[00:29:16] Yes.

[00:29:16] Yeah.

[00:29:17] That was my, my third modular monolith course.

[00:29:19] So the first two are about building modular monoliths for

[00:29:22] developers and maybe architects.

[00:29:24] And then the third one, like I said, it’s for more like, you know, technical

[00:29:27] decision makers and architects that are trying to plan how to, how to fix the

[00:29:31] fact that they’ve gone too far down the microservice rabbit hole, and maybe

[00:29:34] they’d like to back out a little bit, something simpler and cheaper.

[00:29:37] Oh, you, you won course ahead of me on dome train then and to get another

[00:29:41] one out.

[00:29:42] Yeah.

[00:29:42] I’ve got, I’ve got two more that are due at like next month.

[00:29:44] So it’s a race.

[00:29:47] Oh, you’re going to win that race.

[00:29:49] I’ve got like zero time at the moment.

[00:29:50] So I’m struggling, but yeah.

[00:29:52] Uh, it’s quite interesting cause I’ve had a few of the dom train authors on the

[00:29:56] podcast and we always like get onto the topic of how you actually found actually

[00:30:01] creating the course.

[00:30:01] Cause one thing I found a bit different with dom train is like plurals, like

[00:30:04] obviously the authors don’t have the webcams on and that’s one big difference

[00:30:08] for me, like with the dom train courses, the webcams are on.

[00:30:11] And as an author, that’s quite a different experience.

[00:30:13] I’ve not done pluralsight.

[00:30:15] I know you’ve got pluralsight courses, haven’t you?

[00:30:16] Oh yeah.

[00:30:17] A bunch of them.

[00:30:18] How do you find the editing difference when you had to have your webcam on?

[00:30:21] It was, it was very different.

[00:30:22] I was happy with the fact that it was okay to have jump cuts inside the edits.

[00:30:26] If I had to do a perfect take of every clip, that would have been way, way more

[00:30:30] difficult.

[00:30:31] I definitely had to learn a new set of tools because everything I did with

[00:30:33] pluralsight was always with Camtasia.

[00:30:35] And I think you can use Camtasia for, for something with live video, but I

[00:30:39] decided to just follow Nick’s lead.

[00:30:41] And, and use DaVinci resolve, which is a much more capable, I think, editor

[00:30:46] tool for, for video, but also has a much steeper learning curve.

[00:30:49] So it took me a while to get the gist of that, but you know, things like trying

[00:30:52] to make sure that I kind of look more or less the same, at least through a

[00:30:55] single module, even if I didn’t record it all at the same time, like those are

[00:30:58] challenges that you don’t have with pluralsight where you’re just doing

[00:31:01] screencasting, but it does, I think, make for a little bit more of a better

[00:31:05] connection with the audience, with the student where like they will get to feel

[00:31:09] more like they know you, like they’ve seen you.

[00:31:11] You know, talking to them, they get that Jonathan Stark calls that, uh, asymmetric

[00:31:15] intimacy where, you know, people will come up to you and say, oh yeah, I know

[00:31:17] you, I’ve, I’ve listened to your podcast or I’ve seen your videos and, and, you

[00:31:21] know, you’re as the author, you’re like, you, you don’t know them from Adam, but

[00:31:24] like, you know, they, they have this feeling of, of knowing you because of that.

[00:31:27] And I think that’s enhanced by having your face on the screen when

[00:31:30] you’re, when you’re talking to them.

[00:31:32] Yeah, definitely.

[00:31:32] I think for me, the, you mentioned about jump cuts.

[00:31:35] Once I accepted the fact that jump cuts were okay.

[00:31:38] And it was, as you say, it was really helpful that Nick kind of like was yeah.

[00:31:41] Do junkets.

[00:31:41] It’s fine.

[00:31:42] Cause I think he was saying he’d much rather see junk cuts than uncomfortable

[00:31:46] pauses and waiting, thinking for the next thing to say.

[00:31:50] So once I embraced that, I found the quality of my videos improved a lot.

[00:31:54] But, but yeah, when you were saying about making sure that your appearance

[00:31:57] doesn’t change, that’s like my first course I did last year, the Docker one,

[00:32:01] because it took me a lot longer to do, cause it was the first one.

[00:32:03] So as you say, like there’s the learning curve of DaVinci resolve and everything.

[00:32:06] Then I didn’t think about things like even haircuts and stuff.

[00:32:09] So it’s kind of like halfway.

[00:32:11] Yeah.

[00:32:11] I needed to get a haircut.

[00:32:12] So suddenly my hair has completely changed where in the second, I did the

[00:32:16] Kubernetes course this year, right before I started recording, I had a haircut.

[00:32:20] So I knew that I wouldn’t have to have it at the end and just things like that,

[00:32:22] which you just don’t think about, but it’s huge learning curve, but great fun.

[00:32:26] So I’m so glad I did it.

[00:32:27] Cause it’s been such a, an amazing opportunity.

[00:32:30] Yeah.

[00:32:30] It’s been fun.

[00:32:31] It is, it is a lot of work though.

[00:32:32] And I’m totally slammed right now, but I’ve got, like I said, I’ve got two

[00:32:34] courses I’ve got to get finished here soon.

[00:32:36] And then I dunno, I may, I may take a little break from doing more courses

[00:32:39] cause I’m way behind on it.

[00:32:41] A bunch of things.

[00:32:42] Well, you’ve got your consultant.

[00:32:42] Is it nimble pros?

[00:32:44] What was it called?

[00:32:44] That’s right.

[00:32:44] So you’ve got your consulting company as well.

[00:32:46] So yeah, time is a valuable asset.

[00:32:49] Isn’t it?

[00:32:49] Really?

[00:32:49] Yeah.

[00:32:50] I’ve got some clients that would love more of my time, but I’m telling

[00:32:52] them right now I I’m too busy.

[00:32:54] So I’m trying to work on these courses and get them done

[00:32:56] somewhere close to their deadline.

[00:32:57] So that’s the, that’s the challenge.

[00:33:00] Yeah.

[00:33:00] I think saying no is quite a powerful skill, which a lot of people don’t have.

[00:33:04] So I think it’s very good for stress management being say no to things.

[00:33:08] They don’t get too many things up.

[00:33:09] Um, right.

[00:33:10] So.

[00:33:10] Well,

[00:33:11] is there anything else that was like, as I say, I’ve got a happy few calls,

[00:33:13] so there might be things that I’ve not thought of or to ask, is there

[00:33:16] anything else that is worth mentioning around modular monoliths?

[00:33:19] Well, there’s a couple of things that I could cover that are a little

[00:33:22] more on the technical side, if you like, uh, one, one of them, I’ve, I’ve

[00:33:25] done some YouTube videos about this and it’s also covered in my course, but

[00:33:29] one of the benefits of using something like mediator is that you can construct

[00:33:33] your own middleware pipelines inside your application.

[00:33:36] So for example, if you have, you know, validation that you always want to do

[00:33:40] on.

[00:33:41] You know, if you have, you know, an order service class that takes in parameters and

[00:33:47] then every single one of those methods needs to do validation on those parameters to make

[00:33:51] sure that they’re, you know, in range or whatever.

[00:33:53] Uh, if you do embrace this message passing with mediator style, you could send, you know,

[00:33:59] a query or, or a command to the, the order handler, and then you could have a fluent

[00:34:04] validation behavior or whatever type of validation you want to use.

[00:34:07] I use fluent validation and every message will go through that behavior.

[00:34:11] And we’ll, you know, be validated.

[00:34:13] And if it’s invalid, it’ll jump back from that behavior before it even gets to the order

[00:34:17] service or handler that would ultimately execute it.

[00:34:20] Likewise, I have a behavior for logging and, and, you know, so every single call gets

[00:34:25] logged and, and so I don’t have to go and add log statements to every method to say,

[00:34:29] I’m starting and I’m finishing and I took this long or whatever.

[00:34:31] All of that is done by a single behavior that every single query or event or command

[00:34:36] goes through.

[00:34:37] Uh, and so in my logs, I can see like, this is exactly the, the

[00:34:40] path that this request took, you know, from coming into an endpoint and then from there

[00:34:46] going to, you know, a query to a query handler or command to a command handler, et cetera.

[00:34:51] And so I’m a big fan of those behaviors for cross-cutting concerns, things like validation

[00:34:57] and logging, like I mentioned, but there’s, there’s a bunch more.

[00:35:00] If you think about it, you know, you can use it for authentication and authorization.

[00:35:03] You can use it for caching and any number of other things you might come up with.

[00:35:07] Basically, if you’re looking at your, your methods that are actually doing your

[00:35:10] business logic and every one of them starts off with log that I’m starting this

[00:35:14] method, try, uh, you know, maybe do some validation or some guard clauses, do the

[00:35:20] actual work and then have a catch clause and then another log statement.

[00:35:23] Like if every one of them has that same shell of code around the real work, all

[00:35:28] that stuff can get pulled out into these behaviors and use what’s called the chain

[00:35:32] of responsibility pattern, which is exactly what ASP.NET Core does with its middleware.

[00:35:35] So you’re already using that pattern.

[00:35:36] If you’re doing ASP.NET Core, this gives you a way to use it inside your

[00:35:40] application.

[00:35:40] Logic so that it’s not coupled to ASP.NET.

[00:35:45] Yeah, definitely with, as I say, we use mediator quite a lot.

[00:35:48] And that’s now you just described with logs.

[00:35:50] We do the same with traces.

[00:35:52] So do you know, like, um, open telemetry traces where it’s exactly the same concept

[00:35:56] you’ve just described, but for all our media to handle us, we don’t have to make any

[00:36:00] changes there because we’ve got a behavior which just does that and starts and stops

[00:36:06] that trace.

[00:36:06] And we, we throw it into Gryphon, a cloud, so we can see all the traces there.

[00:36:10] And if you were to do that manually for each of the mediator handlers, you’d forget for

[00:36:14] a lot of them.

[00:36:14] But as you say, it’s just, we forget about it because it’s just automatic.

[00:36:19] So yeah, very, very powerful.

[00:36:20] Yeah.

[00:36:21] And then, uh, the other thing that is important with, with modular monoliths, I think, is

[00:36:26] this idea of having something bigger than a class as an encapsulation boundary or even

[00:36:32] an aggregate, right?

[00:36:33] And, and so I talk about this a lot in my DDD courses and some of the workshops I do.

[00:36:37] But when you think about encapsulation boundaries in C-sharp, right?

[00:36:40] Or in your software, right?

[00:36:41] It starts really at the lowest level, right?

[00:36:43] Where you’ve got, you know, a variable is kind of like an encapsulation boundary and

[00:36:47] abstraction.

[00:36:47] And then you get up to like a method and a method has, you know, local variables that

[00:36:51] are only valid there.

[00:36:52] And that, that has value because you, you know, that if you just create a local variable

[00:36:56] and you use it in that method, you’re not going to step on anything else anywhere else

[00:36:59] in your program.

[00:36:59] And that’s very valuable to know.

[00:37:01] And then from there, you can go up to a class and at the class level, you can have private

[00:37:05] and protected and internal and other keywords to kind of restrict access to it.

[00:37:09] But beyond that…

[00:37:10] Right?

[00:37:10] We don’t have another level of granularity really inside of C-sharp.

[00:37:15] And so the, the idea of using the modules as a way to achieve that by having that separate

[00:37:20] contracts project and making it so that I didn’t really mention this, but the main module,

[00:37:24] the, you know, the email sender or the order service, whatever that is, everything in there

[00:37:28] should be pretty much internal so that other things, even if they did make a project reference

[00:37:33] to it, they wouldn’t be able to get to any of the things in there because they’re all

[00:37:37] marked internal.

[00:37:38] And then you can use internals visible to.

[00:37:40] Which is one of the tools that we have available to us to make the internals visible to your

[00:37:44] test project.

[00:37:45] And if necessary, you can make them visible to your contracts project as well.

[00:37:49] Usually you don’t need to, because the way that the dependencies work, but by setting

[00:37:53] it up that way, you make it so that you do have still that encapsulation boundary at

[00:37:57] the module level, which is larger than just individual classes.

[00:38:01] And that’s where you get this big ball of mud problem is because, you know, developers,

[00:38:04] even if they’re great with object oriented programming, the tools aren’t there inside

[00:38:07] a single project.

[00:38:09] To be able to encapsulate.

[00:38:10] Everything in this folder from everything in that folder, right?

[00:38:12] We don’t have anything that says, you know, things in this folder, things in this namespace

[00:38:16] shouldn’t be referenced by things in that namespace, right?

[00:38:19] And so you have to use the project system to, to achieve that.

[00:38:22] Or you can also use something like arc unit, which is a architectural unit test that can

[00:38:26] do it as well, but then you don’t get compile time support.

[00:38:28] You only get it at when you’re in the test.

[00:38:31] Oh, you, you jumped ahead and pinch my dev tip.

[00:38:35] I, there was, I don’t know, I heard about this, but there was, it’s not the one that you

[00:38:38] mentioned.

[00:38:38] There’s something I saw recently.

[00:38:40] By.

[00:38:40] There’s one called Ben Morris called net arc test, which is basically what you’re describing.

[00:38:44] I think.

[00:38:44] Yeah, yeah, that there’s two of them.

[00:38:45] That’s the other one.

[00:38:46] Yep.

[00:38:46] Looking at the read me, it says inspired by arc unit library for Java, but yeah, for

[00:38:51] the listeners benefit, like just reading the description, a fluent API for.net standard

[00:38:55] that can enforce architectural rules in unit tests and just like looking at one of the

[00:39:00] examples.

[00:39:01] So if, if you picture the, and this is for the listeners, cause I know you know it, but

[00:39:05] if you picture like fluent assertions, that kind of fluent chain function kind of syntax,

[00:39:10] and just like reading the first example, it’s that kind of thing where it’s like types

[00:39:14] dot in current domain that resides in namespace, then it’s got a string, which is the namespace

[00:39:19] name should not have dependency on then something else.

[00:39:24] And so it’s doing the, so basically about your dependencies and that feels, I guess,

[00:39:28] to go to my earlier question about for junior developers or people that were less experienced.

[00:39:33] And I think your answer was about having reviews and stuff.

[00:39:36] I guess if it’s something like this is warranted, this is another way to actually.

[00:39:39] Enforce that by just having a set of tests that make sure that you don’t break those

[00:39:44] boundary rules.

[00:39:45] Yep.

[00:39:46] Yep.

[00:39:47] For sure.

[00:39:48] And, and as far as the code reviews go, like the things you would look for in a code review

[00:39:50] would be someone adding a project reference where they shouldn’t or making something public

[00:39:53] that was internal.

[00:39:54] Like, you know, if you’ve got those guardrails in place, they’re just code, right?

[00:39:58] And so someone that has full right access to the code can get rid of the guardrails

[00:40:02] and they could comment out that, that arc unit or net arc unit tests too.

[00:40:05] Right.

[00:40:06] So, you know, you still have to look at the pull requests or the commits that are coming

[00:40:09] in.

[00:40:09] Because someone can, you know, thinking that they’re doing the right thing, bypass some

[00:40:13] of those guardrails.

[00:40:14] Yeah.

[00:40:15] That makes sense.

[00:40:16] So one final question before we wrap up, where did the term modular monolith come from?

[00:40:20] Because I’m hearing it all over the show nowadays.

[00:40:22] And has it actually come from somewhere specific or is it something that’s, I don’t know who

[00:40:27] first coined the term.

[00:40:28] I know it’s been around for a while.

[00:40:29] I’ve got a cartoon I post every now and then on my, on my Twitter.

[00:40:33] That’s like, you know, aliens coming down and telling people that they shouldn’t just

[00:40:36] build microservices.

[00:40:37] They should start with a modular monolith.

[00:40:38] And, you know, even when that cartoon came out, I think it was like in 2019 or 2020,

[00:40:43] it was a term that had been around for a while.

[00:40:44] So it’s, it’s not really new, but it’s becoming more popular, I think, because so many people

[00:40:48] have found themselves in the morass of microservice hell and are trying to find a way out that

[00:40:55] doesn’t lead them back to the big ball of mud hell that they came from.

[00:40:58] And just to call back to that diagram that we kind of mentally talked about with the

[00:41:01] two axes, right, toward, toward the top was things that were modular, toward the right

[00:41:07] was things that had many services.

[00:41:08] The problem a lot of companies come into is they try to go microservices, but they don’t

[00:41:13] decouple them, right?

[00:41:14] So maybe they’ll share a database.

[00:41:16] And so you end up in the bottom right of that quadrant where you’ve built a distributed

[00:41:20] ball of mud or a distributed monolith, which is the worst of all worlds, right?

[00:41:23] You’ve got all the problems of a distributed system and you’re still in a tightly coupled

[00:41:27] big ball of mud problem inside of your architecture.

[00:41:30] So, you know, definitely try and avoid ending up there.

[00:41:33] I will include, if it’s okay with you, that diagram in the show notes just so the listeners

[00:41:37] have some context.

[00:41:38] And also that comic you mentioned about the aliens coming down, I’ll include those in

[00:41:43] the show notes.

[00:41:44] Cool.

[00:41:45] So we are almost running out of time and I’ve already done my dev tip because it came up

[00:41:48] earlier and I think you had a dev tip as well.

[00:41:51] Sure.

[00:41:52] One of the tips I often share is that when you’re doing maintenance programming, you

[00:41:56] should default to writing new code in a new class or in a new method rather than just

[00:42:01] going in and hacking some existing code or adding some code to an existing method.

[00:42:05] Okay.

[00:42:06] So one thing that is preferable is that, especially if you’re building a new class, and this was

[00:42:11] great advice for when you’re working on legacy systems that have huge, you know, thousand

[00:42:14] line long methods and are just a mess, right?

[00:42:17] When you create that new class, you could write unit tests for it because it’s brand

[00:42:21] new.

[00:42:22] You can design it to be unit testable and you can construct it in whatever way makes

[00:42:25] sense to you.

[00:42:26] Name it the way you would name it, you know, design it with all the best practices you

[00:42:29] know or patterns you want to use.

[00:42:31] And then just new it up and call it from whatever, you know, giant method that you had before

[00:42:35] that you were trying to, you know, insert some logic.

[00:42:36] And if you do that long enough, eventually that giant method is going to be composed

[00:42:41] of, you know, smaller classes that are unit testable that only do one thing and the system

[00:42:45] will gradually get better over time.

[00:42:47] It also makes it much easier for you to use things like feature flags and stuff.

[00:42:50] If you’ve got, you know, logic that is, you know, logically separated into its own structure,

[00:42:54] into its own class or new method, then if it was just inline into an already too long

[00:42:59] method.

[00:43:00] So that’s my tip is if you’re doing maintenance programming, try and default to writing new

[00:43:03] code in new classes instead of just editing.

[00:43:06] And doing surgery on existing ugly code.

[00:43:09] Yeah.

[00:43:10] That makes total sense.

[00:43:11] Awesome.

[00:43:12] So was there anything before we wrap up?

[00:43:13] Is there anything else you wanted to cover or mention?

[00:43:15] We’ve mentioned Nimble Pros.

[00:43:16] Any shouts out?

[00:43:17] Yeah.

[00:43:18] I mean, the courses are live on DOMETRAIN.

[00:43:20] I’ve got a whole sample there that I walk you through how to build.

[00:43:23] So I also have a template that you can use to create modules and create a modular monolith

[00:43:28] architecture, which is our dallas slash moduleth, like a modular monolith, moduleth.

[00:43:33] And that’ll be in the show notes too, I’m sure.

[00:43:35] So, yeah.

[00:43:36] That’s probably good.

[00:43:37] Yeah.

[00:43:38] I’ll definitely include links to all the different stuff in the show notes.

[00:43:42] Cool.

[00:43:43] So, well, with that, a massive thank you for joining us on the show.

[00:43:45] It’s been amazing having you on.

[00:43:46] Thanks.

[00:43:47] Glad to be here.

[00:43:48] See you next time.

[00:43:49] And thank you, everyone, for listening.

[00:43:50] And a quick reminder that this podcast is sponsored by Everstack, which is my own company

[00:43:55] providing software development and consultation services.

[00:43:57] For more information, visit everstack.com.

[00:43:59] And if you’re enjoying the podcast, please do help me spread the word on social media.

[00:44:03] I normally use the hashtag unhandled exceptions.

[00:44:05] And I can be found on Twitter at Dracan or X now.

[00:44:08] But the best place to get in touch with me is on Discord.

[00:44:11] And as I mentioned earlier, we’ve got our own Discord community, which is linked to

[00:44:15] on the website.

[00:44:16] And yeah, all the stuff, including that Discord link and the show notes can be found on unhandledexceptionpodcast.com.

[00:44:35] See you next time, everyone.

[00:44:44] Bye.