Chapter 12
Distribution Design Patterns
Introduction
To organize the distribution, the design patterns of this category can be divided into the following two main sections:
Remote Facade: By providing a coarse-grained view of fine-grained objects, effectiveness, and efficiency are increased throughout the network.
Data Transfer Object: It can move data between processes and reduce the number of calls to different methods.
Structure
In this chapter, we will cover the following topics:
Distribution design patterns
Remote Facade
Data Transfer Object
Objectives
In this chapter, you will learn the types of design patterns related to distributed software, such as Remote Facade and Data Transfer Object, and how to communicate in distributed software and move data between different parts.
Distribution design patterns
One of the hot discussions today is the discussion of distributed software, and today the need for distributed software is growing. The important thing is to know the boundaries and limits of distribution. For example, consider the following separations:
Separation of the client from the server
Separation of the server part of the program from the database
Separating the web server from the application server
In addition to the preceding separations, others can also be considered. In all these separations, it is necessary to pay attention to what effect this separation will have on efficiency. When designing the software, the boundaries and limits of distribution should be reduced as much as possible. It cannot be eliminated and must be properly designed and managed in some places. For example, connecting the front-end to the back-end codes may be necessary. To do this, you can use the remote facade design pattern. When there is a need to establish communication between the front and back end, a method will need to move the data. For this purpose, a data transfer object can be used.
Remote facade
Name:
Remote facade
Classification:
Distribution design patterns
Also known as:
---
Intent:
By using this design pattern, by providing a coarse-grained view of fine-grained objects, the effectiveness and efficiency increase throughout the network.
Motivation, Structure, Implementation, and Sample code:
Suppose a requirement is raised, and, in its format, it is requested to provide a web service to collect and display the information of authors and their books. It is only necessary to display the author's name next to the list of his books. There are different ways to collect and display this information. One method is to provide the following web services to the client for use:
Web service for registering and displaying the author's identity information.
Web service for recording and displaying information about the author's books.
The problem with using this structure is that at least 2 HTTP requests are needed to record this information. 2 HTTP requests mean moving data across the network twice, checking authentication and access control information twice, and so on. This volume of work, when the number of simultaneous requests increases, or the number of round trips during the network increases, can greatly overshadow efficiency and effectiveness.
Another way is to provide a coarse-grained view to the client, and the client delivers all the required data to the server in the form of an HTTP request, and the rest of the processing takes place on the server. With these explanations, the following codes can be considered to implement the preceding requirement:
We have the models related to the author and the book as follows:
public class Author
{
public int AuthorId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool IsActive { get; set; }
public ICollection
}
public class Book
{
public int BookId { get; set; }
public string Title { get; set; }
public string Language { get; set; }
public ICollection
}
As it is clear in the preceding models, each author can have several books, and each book can have several authors. According to the stated requirement, using these models will not work well in displaying the information of authors and their books because many of the features in these models will not be needed for display. Based on this, we need models that are the same as the client's needs. Therefore, we are going to define a series of DTOs. These DTOs are defined using the data transfer object design pattern:
public class AuthorDTO
{
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection
}
public class BookDTO
{
public string Title { get; set; }
public string Language { get; set; }
}
In the preceding code, for the sake of simplicity, AuthorId and BookId features are not included in DTO. In real implementations, these two features will probably be needed. Now that the DTOs the client requires are ready, a mechanism for the model to DTO and vice versa is needed. For this, you can use a mapper or any other method. Therefore, we have the following codes for conversion:
public class AuthorAssembler
{
public AuthorDTO ToDTO(Author author)
{
AuthorDTO dto = new()
{
FirstName = author.FirstName,
LastName = author.LastName
};
ConvertBooks(dto, author);
return dto;
}
public void ConvertBooks(AuthorDTO dto, Author model)
{
foreach (var book in model.Books)
{
dto.Books.Add(new BookDTO
{
Title = book.Title,
Language = book.Language
});
}
}
public Author ToModel(AuthorDTO author)
{
Author dto = new()
{
FirstName = author.FirstName,
LastName = author.LastName
};
ConvertBooks(dto, author);
return dto;
}
public void ConvertBooks(Author model, AuthorDTO dto)
{
foreach (var book in dto.Books)
{
model.Books.Add(new Book
{
Title = book.Title,
Language = book.Language
});
}
}
}
As you can see, in the preceding class named AuthorAssembler, there are two methods for converting the model to DTO and DTO to model. To get the list of authors and insert them, the following two methods are defined in the AuthorAssembler class:
public void CreateAuthor(AuthorDTO dto)
{
Author author =ToModel(dto);
author.AuthorId = new Random().Next(1, 100);
CreateBooks(dto.Books, author);
}
private void CreateBooks(ICollection
{
if (dtos != null)
{
if (dtos.Any(x => string.IsNullOrWhiteSpace(x.Title)))
throw new Exception("Book title cannot be null or empty");
foreach (var item in dtos)
{
var book = new Book()
{
Title = item.Title,
Language = item.Language
};
book.BookId = new Random().Next(1, 100);
author.Books.Add(book);
}
}
}
public List
=> DatabaseGateway.GetAuthors().Select(x => ToDTO(x)).ToList();
Note that the implementations done in this class are considered simple for the sake of simplicity. Now, to receive user requests, the coarse-grained view can be defined as follows:
public interface IAuthorService
{
void AddAuthor(AuthorDTO dto);
ICollection
}
public class AuthorService : IAuthorService
{
public void AddAuthor(AuthorDTO dto)
=> new AuthorAssembler().CreateAuthor(dto);
public ICollection
=> new AuthorAssembler().GetAuthors();
}
As the preceding codes show, this view does nothing except translate coarse-grained methods to fine-grained ones. With the existence of AuthorService, there is no need to go back and forth in the network and its related negative effects, and the user's needs can be met with minimal back and forth.
Notes:
This design pattern will not be needed when all transactions are inside a process. But this design pattern can be very useful when the transactions are divided between several processes (within a machine or across the entire web).
The generated coarse-grained view must not contain any domain-related logic. The entire application should work correctly, regardless of the classes associated with these views.
If there are models with the same structure on both sides of the communication, DTO is unnecessary, but in reality, this is almost impossible. Usually, this design pattern is designed and used alongside the data transfer object design pattern.
There may be different methods for communicating with different objects in the design of a coarse-grained view. Having one or more coarse-grained views is a decision that can be made at the time of implementation.
Designed views can be stateless or stateful. If these views need to be stateful, you can use the design patterns in the session state category.
Designed views should usually be good places to control access or manage transactions.
An important feature of this design pattern is several processes or so-called remote use. If we take the feature of being remote from this design pattern, then this design pattern will be very similar to the service layer design pattern.
Consequences: Advantages
Reducing the number of requests during the network will increase productivity and efficiency.
By implementing this design pattern asynchronously, the efficiency can be increased even more.
Consequences: Disadvantages
It will increase the complexity if used in smaller programs or programs only inside a Process.
Applicability:
Improving effectiveness and efficiency in remote communication or outside the current process can be very useful. For example, connecting the front end to the back end in scenarios where the front end is separated from the back end.
Related patterns:
Some of the following design patterns are not related to the Remote Facade design pattern, but to implement this design pattern, checking the following design patterns will be useful:
Data transfer object
Session state
Service layer
Data transfer object
Name:
Data transfer object
Classification:
Distribution design patterns
Also known as:
---
Intent:
Using this design pattern, data can be moved between processes, and the number of calls to different methods can be reduced.
Motivation, Structure, Implementation, and Sample code:
Example 1:
suppose we need to deliver the authors' information and their books to the client to display to the user. There are several methods for this. , the client receives the list of authors once and the list of their books once. As mentioned in the remote facade design pattern, this work increases the number of round trips in the network and affects effectiveness and efficiency.
Another method is to deliver the data to the client as domain models, keeping the round trips to the server to a minimum. The problem with this method is that the structure we deliver to the client may differ from the structure of the domain models.
The data transfer object design pattern helps to deliver the data in the format required by the client while minimizing back and forth to the server.
With the preceding explanations, DTO can be used to implement the preceding scenario. The important point here is how DTO is related to the domain model. The dependence of these two on each other will not be a pleasant event because they can have completely different structures. You can get help from a mapper to solve this problem.
Now for the preceding scenario, the following codes can be considered for DTO and domain model:
public class Author{
public int AuthorId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool IsActive { get; set; }
public ICollection
}
public class AuthorDTO{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Status { get; set; }
public ICollection
}
For example, in the preceding code, the Author class has features such as AuthorId or IsActive. These features are not considered in the DTO definition because the client does not require these features. Also, in the definition of DTO, there is an attribute called Status, which does not exist in the Author model. The same structure is true for Book:
public class BookDTO
{
public string Title { get; set; }
public string Language { get; set; }
}
public class Book
{
public int BookId { get; set; }
public string Title { get; set; }
public string Language { get; set; }
}
Now, for the model and DTO to be connected, as mentioned, you can use a mapper. As follows:
public static class AuthorAssembler
{
public static Author ToModel(this AuthorDTO dto)
=> new Author{
FirstName = dto.FirstName,
LastName = dto.LastName,
IsActive = dto.Status == "Active",
Books = dto.Books.Select(x => x.ToModel()).ToList()
};
public static AuthorDTO ToDTO(this Author model)
=> new AuthorDTO{
FirstName = model.FirstName,
LastName = model.LastName,
Status = model.IsActive ? "Active" : "Deactive",
Books = model.Books.Select(x => x.ToDTO()).ToList()
};
private static Book ToModel(this BookDTO dto)
=> new Book{
Title = dto.Title,
Language = dto.Language
};
private static BookDTO ToDTO(this Book model)
=> new BookDTO{
Title = model.Title,
Language = model.Language
};
}
The preceding implementation is done using extension methods in C#.NET. As can be seen, the model is converted to DTO through the ToDTO method, and the DTO is converted to the model through the ToModel method. With the preceding structure, if we want to return the information of authors and their books to the client, it is enough to convert the result to DTO through the ToDTO method and return it to the client after preparing the model. Also, the methods related to Book in implementing this scenario are considered private because we only wanted to deliver the data as authors. (Each author object has an attribute for books).
Example 2:
Suppose that we want to receive and save the information of authors and their books from the client this time. To do this, separate DTOs can be defined, or existing DTOs can be used. If there is a big difference between read and write DTOs, then defining two different DTO classes would be the right thing to do. That said, the way to record the author's data will be very similar to the mode of reading the author's data.
Notes:
A new record feature was introduced in version 9.0 of the C#.NET programming language. By introducing this feature, DTO can also be called Data Transfer Record (DTR).
A DTO only contains data and does not have any behavior. In this case, the word ViewModel can also be used for DTO. ViewModel does not necessarily only contain data; in MVVM architecture, it can also contain a series of behaviors, in which case the terms ViewModel and DTO cannot be used interchangeably.
This design pattern can also implement the remote facade design pattern.
DTO usually contains more data than the client needs. In this case, the DTO can be used for various requirements, and in this way, it can prevent back and forth to the server. Whether to use the same DTO for requests and responses or to put different DTOs for them is a decision related to the request and response structure. If the structures are similar, a DTO can also be used.
Changeable or not is another decision that should be considered in DTO design. No general rule approves one of these structures and negates the other.
Record Set can be considered as the DTO defined for the database.
DTOs should always be able to be serialized so that they can be transferred along the network. For DTOs to be serializable, we must keep them as simple as possible. To serialize DTO using .NET, it is unnecessary to do much work, and .NET has provided the possibility of serializing to various formats such as JSON or XML.
DTOs can be generated automatically by using code generators. To automatically generate DTO, you can also benefit from reflection. The difference between these two methods is the simplicity and cost of implementation.
Mapper can be used to connect the domain model with DTO.
The lazy load design pattern can also use DTO in asynchronous mode.
Consequences: Advantages
This design pattern creates a loose connection between the domain and presentation layers.
Because of this, the data needed by the client is provided to him with the least back and forth to the server, and the efficiency is improved.
Consequences: Disadvantages
The number of DTOs is also proportionally increased in large programs with a lot of existence. This causes an increase in complexity, the volume of the code, and, finally, an increase in the coding time.
Applicability:
Using this design pattern can be useful to transfer data between the client and the server to minimize the back and forth to the server.
One of the uses of DTO is to transfer data between different software layers.
Related patterns:
Some of the following design patterns are not related to the Data Transfer Object design pattern, but to implement this design pattern, checking the following design patterns will be useful:
Remote facade
Lazy load
Conclusion
In this chapter, you got acquainted with the distribution design patterns, such as remote facades and data transfer object design patterns. You also learned how to design communication in distributed software suitably and efficiently and move the data across this software. In the next chapter, you will learn offline concurrency design patterns.
Join our book's Discord space
Join the book's Discord Workspace for Latest updates, Offers, Tech happenings around the world, New Release and Sessions with the Authors:
https://discord.bpbonline.com