Software Architecture Patterns

Hey Everyone,

Today we’ll be talking about

  • Rob Pike’s 5 famous rules for Software Engineering

    • Rob Pike was one of the designers of the Go programming language. He was also a core member of the Unix team at Bell Labs.

    • He has 5 famous rules of programming that serve as useful heuristics to keep in mind.

    • We’ll go through all 5 rules.

  • A summary of Software Architecture Patterns by Mark Richards. We’ll go the first 3 software architecture patterns from the book.

    • Layered Architecture (N-tier Architecture)

    • Event-Driven Architecture ( Mediator Topology and Broker Topology)

    • Microkernel Architecture (Plug-in Architecture)

  • Plus, a couple awesome tech snippets on

    • A fantastic (free) course by Google on CSS

    • A great article to build your first VR app with Unity

    • An amazing, free textbook for learning Competitive Coding (it’s great for coding interviews too)

We have a solution to our last coding interview question on binary search trees, plus a new question from Google.

Quastor Daily is a free Software Engineering newsletter sends out FAANG Interview questions (with detailed solutions), Technical Deep Dives and summaries of Engineering Blog Posts.

Mark Richards wrote a fantastic book titled Software Architecture Patterns where he goes through 5 common architecture patterns and talks about how to decide if you should use a certain pattern.

You can view an O’Reilly article on the book here where Mark details all 5 software architectures.

We’ll be summarizing the first 3 architectures below.

Layered Architecture (N-tier Architecture)

The most common architecture pattern is the layered architecture pattern (also known as the n-tier architecture pattern).

You’ll see this with monolithic applications, especially since this pattern is the de facto standard for most Java EE apps.

Components within the layered architecture pattern are organized into horizontal layers, where each layer performs a specific role within the application.

Examples of a role can be the Presentation Layer or the Business Logic Layer.

One feature of the layered architecture pattern is the separation of concerns among components. Components within a specific layer will only deal with logic that pertains to that layer.

This makes it easier to develop, test, and maintain.

Event-Driven Architecture

The event-driven architecture pattern is a distributed, asynchronous architecture pattern used to produce highly scalable applications.

The architecture is made up of highly decoupled, single-purpose event processing components that asynchronously receive and process events.

Event-Driven Architectures can be split into two topologies: Mediator Topology and the Broker Topology.

The Mediator Topology

The mediator topology is useful for events that have multiple steps and require some level of orchestration to process the event.

There are four main types of architecture components within the media topology

  • event queues

  • event mediator

  • event channels

  • event processors

The event flow starts with a client sending an event to an event queue, which is used to transport the event to the event mediator.

The event mediator receives the initial event and orchestrates that event by sending additional asynchronous events to event channels to execute each step of the process.

Event processors, which listen on the event channels, receive the event from the event mediator and execute specific business logic to process the event.

The Broker Topology

With the broker topology, there is no central event mediator.

Instead, the message flow is distributed across the event processor components ina. chain-like fashion through a lightweight message broker (RabbitMQ, Kafka, etc.)

The broker topology works well when you have a relatively simple event processing flow and you don’t need central event orchestration.

Microkernel Architecture (Plug-In Architecture)

The microkernel architecture pattern consists of two types of architecture components

  • a core system

  • plug-in modules

Application logic is divided between independent plug-in modules and the core system.

The core system typically contains the minimal functionality required to make the system operational.

The plug-in modules are stand-alone, independent components that contain specialized processing, additional features, and custom code that is meant to enhance or extend the core system to produce additional capabilities.

Many operating systems implement the microkernel architecture pattern, hence the origin of the pattern’s name.

For a list of all 5 Software Architecture Patterns (and detailed explanations on each one), check out the full article.

Quastor Daily is a free Software Engineering newsletter sends out FAANG Interview questions (with detailed solutions), Technical Deep Dives and summaries of Engineering Blog Posts.

Rob Pike is one of the designers of the Go programming Language and was also a core member of the Unix team at Bell Labs (he was the co-author of The Unix Programming Environment with Brian Kernighan).

He’s also known for his 5 Rules of Programming.

Here’s the five rules…

  1. You can't tell where a program is going to spend its time. Bottlenecks occur in surprising places, so don't try to second guess and put in a speed hack until you've proven that's where the bottleneck is.

  2. Measure. Don't tune for speed until you've measured, and even then don't unless one part of the code overwhelms the rest.

  3. Fancy algorithms are slow when n is small, and n is usually small. Fancy algorithms have big constants. Until you know that n is frequently going to be big, don't get fancy. (Even if n does get big, use Rule 2 first.)

  4. Fancy algorithms are buggier than simple ones, and they're much harder to implement. Use simple algorithms as well as simple data structures.

  5. Data dominates. If you've chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming.

Tech Snippets

  • Getting Started with Unity and Virtual Reality - Facebook is betting big on VR (Zuck just renamed the company to Meta, for Metaverse). Therefore, it might be a good time to learn how to build VR games and apps.This is a great guide that walks you through creating a basic game for Oculus with Unity.

  • Learn CSS - If you’re like me, you have to constantly google random bits of CSS that you’ve forgotten.This is an awesome (free) CSS course by Google that goes through the Box Model, Selectors, Specificity, Inheritance, Flexbox, Grid, and all the other important concepts in CSS.

  • Competitive Programmer's Handbook - This is an awesome free textbook that goes through all the major concepts and patterns you need to know for competitive programming. It’s also extremely helpful for coding interviews!

Interview Question

Design a data structure that supports adding new words and finding if a string matches any previously added string.

Your data structure should implement two methods

  • addWord(word) - Adds word to the data structure

  • search(word) - Returns true if there is any string in the data structure that matches word. word may contain dots where a dot can be matched with any letter (a dot represents a wildcard).

We’ll send a detailed solution in our next email, so make sure you move our emails to primary, so you don’t miss them!

Gmail users—move us to your primary inbox

  • On your phone? Hit the 3 dots at the top right corner, click "Move to" then "Primary"

  • On desktop? Back out of this email then drag and drop this email into the "Primary" tab near the top left of your screen

Apple mail users—tap on our email address at the top of this email (next to "From:" on mobile) and click “Add to VIPs”

Previous Solution

As a reminder, here’s our last question

You are given an integer n.

Return the number of structurally unique binary search trees that have n nodes of unique values from 1 to n.

Solution

We can solve this question with Dynamic Programming.

We’ll give the top-down and the bottom-up solutions.

We’ll have a recursive function that takes in n, where n is the number of nodes in the BST (where the nodes have values from 1 to n).

We’ll keep track of the number of structurally unique binary search trees with the variable trees (trees is an integer).

Then, we have a loop of integers from 1 to n.

For each integer, we’ll set that as the root of our BST.

Then, we’ll have a right tree which has the nodes from 0 to the root (not including the root). The left tree will consist of nodes from root to n (not including the root).

We can use our recursive function to find the number of structurally unique right trees and the number of structurally unique left trees.

Then, we’ll multiply the number of right trees * the number of left trees (because it’s a combination).

We can add this product to our trees variable that is keeping track of the number of structurally unique BSTs for n.

After our loop iterates through all the integers from 1 to n, we can return trees.

There will be quite a few repeating subproblems with this approach, so we’ll add a memo table to cache our computations.

The previous solution was the top-down dynamic programming solution.

We can flip the question around and use an array as a dp table and keep track of our past computations in there.

Here’s the bottom-up dynamic programming solution.

What’s the time and space complexity of our solutions? Reply back with your answer and we’ll tell you if you’re right/wrong.