JavaScript Design and Architectural Patterns

Tony Kim
6 min readSep 5, 2020

--

September 4th, 2020

One of the most important skills in software engineering is writing clean and efficient code. This can be achieved in many ways such as through functional components, utilizing existing modules, and avoiding redundant code. Digging deeper, great software engineers follow these conventions through proven design and architecture patterns.

So what are design and architecture patterns?

Design patterns are like “recipes” to guide you on how you should structure your code base. Following a design pattern allows you to organize your code, reduce bugs, and allow other engineers to easily follow your work.

Architectural patterns are relatively similar to design patterns except it focuses on the broader scope of finding reusable solutions to commonly occurring problems in software design.

In this article, I will go over some common design and architecture patterns every software engineer should be familiar with.

Design Patterns

Before diving into the different examples of commonly used design patterns, I would like to go over the three categories of design patterns that exists:

  1. Creational: This category focuses on object creation mechanisms which optimize object creation and control object creation.
    Examples: Factory, Builder, Singleton, Abstract, and Prototype
  2. Structural: This category focuses on object relationships. They ensure that if one part of a system changes, the entire system doesn’t need to change along with it.
    Examples: Adapter, Decorator, Composite, and Bridge
  3. Behavioral: This category recognize, implement, and improve communication between disparate objects in a system. They ensure disparate parts of a system have synchronized information.
    Examples: Command, Momento, and Observer

Creational Design Patterns:

  1. Factory Pattern: One of the most commonly used creational patterns that is easy to use and learn. Does not explicitly require us to use a constructor. This pattern provides a generic layout for creating objects, where we can specify the type of factory object we wish to be created.

Code Example:

Factory Pattern

2. Singleton Pattern: This is a pattern that allows a class to hold a method which creates a new instance of the class if one doesn’t exist. If the instance does exist, it returns a reference to that object.

Code Example:

Singleton Pattern

3. Prototype Pattern: This pattern creates objects based on a template of an existing object through cloning.

Code Example:

Prototype Pattern

4. Constructor Pattern: Initialize an object with some set of default and/or sent-in values.

Code Example:

Constructor Pattern

Structural Design Patterns

  1. Adapter Pattern: This pattern allows objects/classes to function together which normally couldn’t due to their incompatibility. The adapter translates calls to its interface into calls to the original interface and the code required.

Code Example:

Part 1 of Adapter Pattern
Part 2 of Adapter Pattern

2. Decorator Pattern: This pattern has the ability to add behavior to existing classes in a system dynamically. Using this pattern allows code reusability.

Code Example:

Part 1 of Decorator Pattern
Part 2 of Decorator Pattern

Behavioral Design Patterns

  1. Command Pattern: This pattern encapsulates method invocation, requests, or operations into a single object and gives us the ability to both parameterize and pass method calls around that can be executed at our discretion. Another feature of this pattern is that it allows us to decouple objects invoking the action from the objects which integrate them.

Code Example:

Command Pattern

2. Observer Pattern: This pattern allows an object (also known as the “subject”) to maintain a list of other objects depending on it (also known as the “observers”), automatically notifying them of any changes to the state.

Code Example:

Observer Pattern

Architecture Patterns

In this section, I will be discussing the most commonly used architecture patterns:

  • Model-View-Controller (MVC)
  • Model-View-Presenter (MVP)
  • Monolithic
  • Micro-service
  1. Model-View-Controller (MVC): This is a very commonly used pattern used by many frameworks such as Rails and Node.js/Express.js. It is a pattern that separates the concerns of applications into three logical components.
  • Model: This component is responsible for all data-related logic that the users work with. The model structures how the data should be organized, as well as sets the rules and methods for storing and accessing it.
  • Views: This component is responsible for managing the presentation of the data. Another way of thinking about this is that it presents the UI logic of the application.
  • Controller: This component is responsible for communicating between the Model and View components.
MVC Pattern

2. Model-View-Presenter (MVP): Derived from the MVC model, it is primarily used to build user interfaces by separating the presentation layer from the logic. In MVP, the Presenter component is the “middle-man”.

  • Model: This component is responsible for all data-related logic that the users work with. The model structures how the data should be organized, as well as sets the rules and methods for storing and accessing it.
  • View: Compared to the View in the MVC model, in MVP, the view is more loosely coupled to the model. In detail, the presenter is in charge for binding the model to the view.
  • Presenter: This component contains all the UI business logic for the view. Any invocations that happen from the View are directly handled by the Presenter. The Presenter is decoupled from the View and communicates to it through a interface.
MVP Pattern

3. Monolithic Architecture: This architecture means that your app or project code base is written in a “central” location or one cohesive unit of code. All the components are designed to work together in which they all share memory space and resources.

Advantages:

  • Allows Cross-Cutting Concerns: logging, rate limiting, and security features (audit trails and DOS protection).
  • Easy to hook up components.
  • Has performance advantages since shared memory access is faster.

Disadvantages:

  • Tend to get tightly coupled and entangled when application size expands. This makes it difficult to isolate services when scaling.
  • Difficult to understand since there a lot of dependencies and side-effects.

4. Microservice Architecture: This architecture means that your app or project code base is written in separate components. The code base is made up of smaller, independent applications that are able to have their own memory space and can scale independently.

Advantages:

  • Highly organized since each microservice has a specific purpose. Each component is not concerned of other duties outside its environment.
  • Decoupled services are easier to recompose and reconfigure. This allows flexibility to serve many different applications such as serving web clients and API.

Disadvantages:

  • Going to run into cross-cutting concerns during development.
  • Need to incur the overhead of the independent modules for each cross-cutting concern. Another option to resolve this issue is by encapsulating cross-cutting concerns in another service environment.

If you are interested in learning more software design and architectural patterns, please check out my GitHub repository: https://github.com/hjkmines/algorithms-datastructure-challenges

If you would like to learn more about my experience and projects I have completed, please view my LinkedIn, portfolio website, and/or GitHub profile:

LinkedIn: https://www.linkedin.com/in/hyung-kim/

Portfolio Website: https://hjkmines.github.io/my-portfolio-website/

GitHub: https://github.com/hjkmines

Please feel free to reach out to me for any questions or inquires through my email: hjkmines@gmail.com.

--

--