During Orlando Code Camp 2024, I really enjoyed a talk by Joseph Guadagno on architectural patterns. Joseph serves as the current president of the Microsoft DotNet Foundation, the organization that supports the growth of the DotNet open-source ecosystem. It provided practical reminders and insightful new ideas on tools software teams can reach for. Joseph emphasized the idea that all patterns have benefits and trade-offs that teams should discuss openly before adopting. In general, I want to thank the organizers of Orlando Code Camp for organizing sessions like this. The experience of comparing notes and war stories with other architects was energizing. I greatly appreciated Mr. Guadangno’s storyteller presentation style. It is important to select the right pattern for the application’s specific needs. The following outlines some of the discussion points of the talk:
You can also find his presentation slides here:
https://www.josephguadagno.net/presentations/application-architecture-patterns
- 3-tier architecture: This is a common architecture pattern that separates the application into three layers: presentation, logic, and data. This strategy tends to have three layers: presentation, logic layer, and database. In the classic implementation of this strategy, strong coupling between the logic layer and data exists which limits unit testing of logic opportunities.
- MVC pattern: This pattern separates the application into three components: model, view, and controller. In my career, I enjoyed seeing Microsoft implement the ASP.NET Core MVC pattern that encouraged the idea of dependency injection to improve the opportunities for unit tests at the controller layer(controller area) or data services layer(model area). By keeping view components separated, teams have the opportunity to find component reuse opportunities.
- Event-driven architecture: This architecture pattern decouples the components of an application by using events to communicate between them. This pattern emphasizes the concept of publishing and subscribing to event messages to facilitate the work of the application. The speaker used the example from social media. If you hit the “like” button on a content element, the system can report that event into a message queue. Various other sub-systems that subscribe to the “like tracking” topic can receive the message and react appropriately. (i.e. updating database records or signaling push notifications.)
-
CQRS pattern: This pattern separates the read and write operations of an application into separate models. CQRS stands for command query responsibility segregation. As you reflect upon designing the responsibilities of your services, you can consider creating services that specialize in data mutation (i.e. commands). This might include adding, updating, or deleting data. Other services can focus on the query of your system. In some situations, you might push this idea further to encourage database representations optimized for scalable query operations or heavy reporting workloads. This strategy helps protect operational systems.
-
Event sourcing: This pattern records all events that occur in an application and uses those events to rebuild the materialized state of those events. Let’s consider your bank account. The system can calculate the balance of an account based on processing all debits and credits. Using the debit and credit data of the account, the system can create a materialized view of your account balance on any given date in the past. Simulation systems might consider using event sourcing by logging the commands and queries of various actors in the system. With a robust accounting log of all mutations and queries by actor, we potentially can help users peak at the state of the system at arbitrary points in time. I do like the idea of tracking the queries and commands. It would teams troubleshoot situations faster since you have a log of user actions. To learn more about this pattern, check out https://learn.microsoft.com/en-us/azure/architecture/patterns/event-sourcing
-
Plugin pattern: This pattern allows you to modify behavior application behavior with minimal edits to existing code. The speaker mentioned that products like Microsoft Word or Salesforce tend to use the plugin pattern a great deal. This might become my favorite pattern to reflect upon. As a user, I love getting to extend Google Chrome using Chrome extensions. In this case, I don’t re-compile anything and stuff just works. I admire systems like NodeRed that have run-time extensibility in the same way. According to Martin Fowler, the host application needs to model some key interfaces or contracts. Plugins to the host system must provide implementation of the interfaces enriching the host application user experience. To learn more about this pattern, check out the following: https://martinfowler.com/eaaCatalog/plugin.html
-
Modular monolith: A modular monolith is a software architecture that combines features of both monolithic and microservices architectures. It essentially structures the application as a single unit (like a traditional monolith) but organizes its internal components into modules with specific functionalities. These modules are loosely coupled and highly cohesive, meaning they have minimal dependencies on each other and focus on a single area of concern. In some contexts, microservice architecture can feel overly complex to design and maintain. This feels like an interesting middle ground to consider.
-
To learn more, check out the following blog post to review the pros and cons of this idea: https://www.wearecogworks.com/blog/understanding-architecture-reasons-to-build-a-modular-monolith-first/
-
To learn more about Microservices, check out this free ebook: Architecture for containerized applications using .NET
-
-
Strangler Fig: The Strangler Fig pattern, also known as the Strangler pattern, is a software design approach used for gradually migrating and replacing legacy systems with new, modern architectures. In this pattern, the team would introduce an application gateway surface that receives all requests. In the initial state, all application requests continue to route to the legacy system in question. The team defines a modular set of interfaces representing new features and refactored modernized code. At this point, the team has the opportunity to improve critical technical debt, transform to new technology, and increase test automation. When the new modernized module becomes ready for release, the team can edit the application gateway to route requests to the new module and sub-system. To learn more about this pattern, check out https://learn.microsoft.com/en-us/azure/architecture/patterns/strangler-fig
Leave a Reply