How to implement a serverless architecture on Azure - Part 4

In this article, we kick things off by implementing our toy example in a monolithic fashion.

Disclaimer

This example will intentionally remain simple and exaggerated for the sake of clarity and illustration.

Keep in mind that in monolithic architectures, there is a single code base, and thus, we must satisfy everyone with a single model. As a result, our C# model will probably appear as follows.

 1public class Book
 2{
 3    public string ISBN { get; set; }
 4    public string Title { get; set; }
 5    public int PageCount { get; set; }
 6    public string SubjectMatter { get; set; }
 7    public List<string> Categories { get; set; }
 8    public DateTime Deadline { get; set; }
 9    public string ImageInTheCover { get; set; }
10    public string Blurb { get; set; }
11    public List<string> Audiences { get; set; }
12    public double Weight { get; set; }
13    public double Height { get; set; }
14    public double Width { get; set; }
15    public List<string> Metadata { get; set; }
16    public List<string> Keywords { get; set; }
17}

At this stage, the first issues are already emerging: as mentioned in the previous post, the marketer doesn't care about the deadline. As for the transporter, the subject matter or the page count are the least of their concerns. Unfortunately, they have to use the same model and deal with their neighbor's problems.

And this issue will propagate to the other layers of the software, somewhat akin to the way water spreads. Let's indeed examine what is happening in the data layer.

1public interface IBookRepository
2{
3    List<Book> GetAllBooks();
4
5    Book GetBookByISBN(string isbn);
6
7    //...
8}

There doesn't appear to be anything particularly surprising in this class. We're simply collecting all the books from the catalog or retrieving a specific book by its ISBN. However, it's important to examine the implementation more closely. For often, the devil hides in the details.

 1public class SqlServerBookRepository : IBookRepository
 2{
 3    public List<Book> GetAllBooks()
 4    {
 5        var sql = "select * from Books";
 6
 7        // Query the database...
 8    }
 9
10    public Book GetBookByISBN(string isbn)
11    {
12        var sql = $"select * from Books where ISBN = '{isbn}'";
13
14        // Query the database...
15    }
16}

Once again, there's nothing particularly surprising here. However, when the marketer wants to retrieve all the books (or a specific one), she receives all the properties of the books, including those she doesn't actually require. It's essential to remember that these unnecessary properties consume bandwidth and can potentially hinder the application's performance.

One common solution to this problem, observed in various projects, involves defining additional methods in the interface that include only the most frequently used properties.

 1public interface IBookRepository
 2{
 3    List<Book> GetAllBooks();
 4
 5    List<Book> GetAllBooksLight();
 6
 7    Book GetBookByISBN(string isbn);
 8
 9    //...
10}
 1public class SqlServerBookRepository : IBookRepository
 2{
 3    ...
 4
 5    public List<Book> GetAllBooksLight()
 6    {
 7        var sql = "select ISBN, Title, SubjectMatter from Books";
 8
 9        // Query the database...
10    }
11
12    ...
13}

However, over time, this approach proves most beneficial only in the initial stages of the project. Then, the need for additional properties tends to emerge, and each party is tempted to introduce further ones. Consequently, an increasing number of methods are added to the interface, each tailored to a specific business requirement. This can lead to unnecessary complexity within the domain and obscure the overall project.

When software doesn't have a clean design, developers dread even looking at the existing mess, much less making a change that could aggravate the tangle or break something through an unforeseen dependency.
Eric Evans (Domain-Driven Design 2003)

And this is how software projects often become unmanageable and cease to evolve...

Add the Author concept

Now, consider a scenario where the company intends to introduce a new concept: a book can have one or multiple authors, which is a fairly natural extension. However, the question arises again – what does the concept of an author mean to a marketer or an SEO professional ? To the transporter, this concept might appear entirely irrelevant, yet they must contend with this cumbersome code. The cycle of issues repeats itself.

 1public class Author
 2{
 3    public string Name { get; set; }
 4
 5    // Properties for the marketer
 6    public string Reputation { get; set; }
 7    //...
 8
 9    // Properties for teh SEO's professional
10    public string TwitterAccount { get; set; }
11    //...
12}

In summary, introducing a new concept has a single consequence: it adds complexity to the model, resulting in classes that encompass a wide range of functionalities, along with unpredictable side effects. As developers, we have consistently encountered these issues throughout our careers.

How to save a book ?

We could provide numerous examples of challenges with this architecture: addressing permissions and access rights, achieving efficient and rapid deployment, handling the departure of a senior developer, and more. However, in this final section, we will highlight a specific aspect where monolithic architectures are simpler to manage than microservices: the ease of saving an object due to the presence of a single codebase.

1public interface IBookRepository
2{
3    // ...
4
5    void SaveBook(Book book);
6
7    //...
8}
 1public class SqlServerBookRepository : IBookRepository
 2{
 3    // ...
 4
 5    public void SaveBook(Book book)
 6    {
 7        var sql = $"update Books set Title = '{book.Title}', ... where ISBN = '{book.ISBN}'";
 8
 9        // Execute...
10    }
11}

Everyone is expected to use this method to update the database, and in theory, it should facilitate the rapid identification of problems in case of update failures or unintended side effects.

In the next part, we will endeavor to propose a solution to the issues discussed here by introducing several additional concepts related to software design. Stay tuned.