Table of Contents
1. Architecting ASP.NET Core Applications, Third Edition: An atypical design patterns guide for .NET 8, C# 12, and beyond
2. 1 Introduction
I. Before you begin: Join our book community on Discord
II. What is a design pattern?
III. Anti-patterns and code smells
i. Anti-patterns
ii. Code smells
IV. Understanding the web – request/response
V. Getting started with .NET
i. .NET SDK versus runtime
ii. .NET 5+ versus .NET Standard
iii. Visual Studio Code versus Visual Studio versus the command-line interface
iv. Technical requirements
VI. Summary
VII. Questions
VIII. Further reading
3. 2 Automated Testing
I. Before you begin: Join our book community on Discord
II. Introduction to automated testing
i. Unit testing
ii. Integration testing
iii. End-to-end testing
iv. Other types of tests
v. Picking the right test style
III. Testing approaches
i. TDD
ii. ATDD
iii. BDD
iv. Refactoring
v. Technical debt
IV. Testing techniques
i. White-box testing
ii. Black-box testing
iii. Grey-box testing
iv. White-box vs. Black-box vs. Grey-box testing
v. Conclusion
V. Test case creation
i. Equivalence Partitioning
ii. Boundary Value Analysis
iii. Decision Table Testing
iv. State Transition Testing
v. Use Case Testing
VI. How to create an xUnit test project
VII. Key xUnit features
i. Facts
ii. Assertions
iii. Theories
iv. Closing words
VIII. Arrange, Act, Assert
IX. Organizing your tests
i. Unit tests
ii. Integration tests
X. Writing ASP.NET Core integration tests
i. Classic web application
ii. Minimal hosting
XI. Important testing principles
XII. Summary
XIII. Questions
XIV. Further reading
4. 3 Architectural Principles
I. Before you begin: Join our book community on Discord
II. Separation of concerns (SoC)
III. Don’t repeat yourself (DRY)
IV. Keep it simple, stupid (KISS)
V. The SOLID principles
i. Single responsibility principle (SRP)
ii. Open/Closed principle (OCP)
iii. Liskov substitution principle (LSP)
iv. Interface segregation principle (ISP)
v. Dependency inversion principle (DIP)
VI. Summary
VII. Questions
VIII. Further reading
5. 4 REST APIs
I. Before you begin: Join our book community on Discord
II. REST & HTTP
i. HTTP methods
ii. HTTP Status code
iii. HTTP headers
iv. Versioning
v. Wrapping up
III. Data Transfer Object (DTO)
i. Goal
ii. Design
iii. Conceptual examples
iv. Conclusion
IV. API contracts
i. Code-first API Contract
ii. Wrapping up
V. Summary
VI. Questions
VII. Further reading
VIII. Answers
6. 5 Minimal API
I. Before you begin: Join our book community on Discord
II. Top-level statements
III. Minimal Hosting
IV. Minimal APIs
i. Map route-to-delegate
ii. Configuring endpoints
iii. Leveraging endpoint filters
iv. Leveraging the endpoint filter factory
v. Organizing endpoints
V. Using Minimal APIs with Data Transfer Objects
i. Goal
ii. Design
iii. Project – Minimal API
iv. Conclusion
VI. Summary
VII. Questions
VIII. Further reading
IX. Answers
7. 6 Model-View-Controller
I. Before you begin: Join our book community on Discord
II. The Model View Controller design pattern
i. Goal
ii. Design
iii. Anatomy of ASP.NET Core web APIs
iv. Conclusion
III. Using MVC with DTOs
i. Goal
ii. Design
iii. Project – MVC API
iv. Conclusion
IV. Summary
V. Questions
VI. Further reading
VII. Answers
8. 7 Strategy, Abstract Factory, and Singleton Design Patterns
I. Before you begin: Join our book community on Discord
II. The Strategy design pattern
i. Goal
ii. Design
iii. Project – Strategy
iv. Conclusion
III. The Abstract Factory design pattern
i. Goal
ii. Design
iii. Project – Abstract Factory
iv. Project – The mid-range vehicle factory
v. Impacts of the Abstract Factory
vi. Conclusion
IV. The Singleton design pattern
i. Goal
ii. Design
iii. An alternate (better) way
iv. Code smell – Ambient Context
v. Conclusion
V. Summary
VI. Questions
VII. Answers
9. 8 Dependency Injection
I. Before you begin: Join our book community on Discord
II. What is dependency injection?
i. The composition root
ii. Striving for adaptability
iii. Understanding the use of the IoC container
iv. The role of an IoC container
v. Code smell – Control Freak
vi. Object lifetime
vii. Registering our dependencies
viii. Registering your features elegantly
ix. Using external IoC containers
III. Revisiting the Strategy pattern
i. Constructor injection
ii. Property injection
iii. Method injection
iv. Project – Strategy
v. Conclusion
IV. Understanding guard clauses
V. Revisiting the Singleton pattern
i. Project – Application state
ii. Project – Wishlist
iii. Conclusion
VI. Understanding the Service Locator pattern
i. Project – ServiceLocator
ii. Conclusion
VII. Revisiting the Factory pattern
i. Project – Factory
VIII. Summary
IX. Questions
X. Further reading
XI. Answers
10. 9 Options, Settings, and Configuration
I. Before you begin: Join our book community on Discord
II. Loading the configuration
III. Learning the building blocks
i. IOptionsMonitor<TOptions>
ii. IOptionsFactory<TOptions>
iii. IOptionsSnapshot<TOptions>
iv. IOptions<TOptions>
IV. Project – CommonScenarios
i. Manual configuration
ii. Using the settings file
iii. Injecting options
iv. Named options
v. Reloading options at runtime
V. Project – OptionsConfiguration
i. Creating the program
ii. Configuring the options
iii. Implementing a configurator object
iv. Adding post-configuration
v. Using multiple configurator objects
vi. Exploring other configuration possibilities
VI. Project – OptionsValidation
i. Eager validation
ii. Data annotations
iii. Validation types
VII. Project – OptionsValidationFluentValidation
VIII. Workaround – Injecting options directly
IX. Project – Centralizing the configuration
X. Using the configuration-binding source generator
i. Project – ConfigurationGenerators: Part 1
XI. Using the options validation source generator
i. Project – ConfigurationGenerators: Part 2
XII. Using the ValidateOptionsResultBuilder class
i. Project - ValidateOptionsResultBuilder
XIII. Summary
XIV. Questions
XV. Further reading
XVI. Answers
11. 10 Logging patterns
I. Before you begin: Join our book community on Discord
II. About logging
III. Writing logs
IV. Log levels
V. Logging providers
VI. Configuring logging
VII. Structured logging
VIII. Summary
IX. Questions
X. Further reading
XI. Answers
12. 11 Structural Patterns
I. Before you begin: Join our book community on Discord
II. Implementing the Decorator design pattern
i. Goal
ii. Design
iii. Project – Adding behaviors
iv. Project – Decorator using Scrutor
v. Conclusion
III. Implementing the Composite design pattern
i. Goal
ii. Design
iii. Project – BookStore
iv. Conclusion
IV. Implementing the Adapter design pattern
i. Goal
ii. Design
iii. Project – Greeter
iv. Conclusion
V. Implementing the Façade design pattern
i. Goal
ii. Design
iii. Project – The façades
iv. Conclusion
VI. Summary
VII. Questions
VIII. Further reading
IX. Answers
13. 12 Behavioral Patterns
I. Before you begin: Join our book community on Discord
II. Implementing the Template Method pattern
i. Goal
ii. Design
iii. Project – Building a search machine
iv. Conclusion
III. Implementing the Chain of Responsibility pattern
i. Goal
ii. Design
iii. Project – Message interpreter
iv. Conclusion
IV. Mixing the Template Method and Chain of Responsibility patterns
i. Project – Improved message interpreter
ii. Project – A final, finer-grained design
iii. Conclusion
V. Summary
VI. Questions
VII. Answers
14. 13 Understanding the Operation Result Design Pattern
I. Before you begin: Join our book community on Discord
II. The Operation Result pattern
i. Goal
ii. Design
iii. Project – Implementing different Operation Result patterns
iv. Advantages and disadvantages
III. Summary
IV. Questions
V. Further reading
VI. Answers
15. 14 Layering and Clean Architecture
I. Before you begin: Join our book community on Discord
II. Introducing layering
i. Classic layering model
ii. Splitting the layers
iii. Layers versus tiers versus assemblies
III. Responsibilities of the common layers
i. Presentation
ii. Domain
iii. Data
IV. Abstract layers
V. Sharing the model
VI. Clean Architecture
VII. Implementing layering in real life
i. To be or not to be a purist?
ii. Building a façade over a database
VIII. Summary
IX. Questions
X. Further reading
XI. Answers
16. 15 Object Mappers, Aggregate Services, and Façade
I. Before you begin: Join our book community on Discord
II. Object mapper
i. Goal
ii. Design
iii. Project – Mapper
iv. Conclusion
III. Code smell – Too many dependencies
IV. Pattern – Aggregate Services
V. Pattern – Mapping Façade
VI. Project – Mapping service
VII. Project – AutoMapper
VIII. Project – Mapperly
IX. Summary
X. Questions
XI. Further reading
XII. Answers
17. 16 Mediator and CQRS Design Patterns
I. Before you begin: Join our book community on Discord
II. A high-level overview of Vertical Slice Architecture
III. Implementing the Mediator pattern
i. Goal
ii. Design
iii. Project – Mediator (IMediator)
iv. Project – Mediator (IChatRoom)
v. Conclusion
IV. Implementing the CQS pattern
i. Goal
ii. Design
iii. Project – CQS
iv. Conclusion
V. Code smell – Marker Interfaces
i. Metadata
ii. Dependency identifier
VI. Using MediatR as a mediator
i. Project – Clean Architecture with MediatR
ii. Conclusion
VII. Summary
VIII. Questions
IX. Further reading
X. Answers
18. 17 Getting Started with Vertical Slice Architecture
I. Before you begin: Join our book community on Discord
II. Anti-pattern – Big Ball of Mud
III. Vertical Slice Architecture
i. What are the advantages and disadvantages?
ii. Project – Vertical Slice Architecture
iii. Conclusion
IV. Continuing your journey: A few tips and tricks
V. Summary
VI. Questions
VII. Further reading
VIII. Answers
19. 18 Request-EndPoint-Response (REPR) and Minimal APIs
I. Before you begin: Join our book community on Discord
II. Request-EndPoint-Response (REPR) pattern
i. Goal
ii. Design
iii. Project – SimpleEndpoint
iv. Conclusion
III. Project – REPR—A slice of the real-world
i. Assembling our stack
ii. Dissecting the code structure
iii. Exploring the shopping basket
iv. Managing exception handling
v. Grey-box testing
IV. Summary
V. Questions
VI. Further reading
VII. Answers
20. 19 Introduction to Microservices Architecture
I. Before you begin: Join our book community on Discord
II. What are microservices?
i. Cohesive unit of business
ii. Ownership of data
iii. Microservice independence
III. An introduction to event-driven architecture
i. Domain events
ii. Integration events
iii. Application events
iv. Enterprise events
v. Conclusion
IV. Getting started with message queues
i. Conclusion
V. Implementing the Publish-Subscribe pattern
i. Message brokers
ii. The event sourcing pattern
iii. Example
iv. Conclusion
VI. Introducing Gateway patterns
i. Gateway Routing pattern
ii. Gateway Aggregation pattern
iii. Backend for Frontend pattern
iv. Mixing and matching gateways
v. Conclusion
VII. Project – REPR.BFF
i. Layering APIs
ii. Running the microservices
iii. Creating typed HTTP clients using Refit
iv. Creating a service that serves the current customer
v. Features
vi. Conclusion
VIII. Revisiting the CQRS pattern
i. Advantages and potential risks
ii. Conclusion
IX. Exploring the Microservice Adapter pattern
i. Adapting an existing system to another
ii. Decommissioning a legacy application
iii. Adapting an event broker to another
iv. Conclusion
X. Summary
XI. Questions
XII. Further reading
XIII. Answers
21. 20 Modular Monolith
I. Before you begin: Join our book community on Discord
II. What is a Modular Monolith?
i. What are traditional Monoliths?
ii. What are microservices?
III. Advantages of Modular Monoliths
IV. Key Components of a Modular Monolith
V. Implementing a Modular Monolith
i. Planning the project
ii. Defining our stack
VI. Project—Modular Monolith
i. Sending events from the catalog module
ii. Consuming the events from the basket module
iii. Inside the aggregator
iv. Exploring the REST API HttpClient
v. Sending HTTP requests to the API
vi. Validating the existence of a product
VII. Transitioning to Microservices
VIII. Challenges and Pitfalls
IX. Conclusion
X. Questions
XI. Further reading
XII. An end is simply a new beginning
XIII. Answers
Before you begin: Join our book community on Discord
Give your feedback straight to the author himself and chat to other early readers on our Discord server (find the "architecting-aspnet-core-apps-3e" channel under EARLY ACCESS SUBSCRIPTION).
https://packt.link/EarlyAccess

The goal of this book is not to create yet another design pattern book; instead, the chapters are organized according to scale and topic, allowing you to start small with a solid foundation and build slowly upon it, just like you would build a program.Instead of a guide covering a few ways of applying a design pattern, we will explore the thought processes behind the systems we are designing from a software engineer’s point of view.This is not a magic recipe book; from experience, there is no magical recipe when designing software; there are only your logic, knowledge, experience, and analytical skills. Let’s define “experience” as your past successes and failures. And don’t worry, you will fail during your career, but don’t get discouraged by it. The faster you fail, the faster you can recover and learn, leading to successful products. Many techniques covered in this book should help you achieve success. Everyone has failed and made mistakes; you aren’t the first and certainly won’t be the last. To paraphrase a well-known saying by Roosevelt: the people that never fail are the ones who never do anything.At a high level:
This book explores basic patterns, unit testing, architectural principles, and some ASP.NET Core mechanisms.
Then, we move up to the component scale, exploring patterns oriented toward small chunks of software and individual units.
After that, we move to application-scale patterns and techniques, exploring ways to structure an application.
Some subjects covered throughout the book could have a book of their own, so after this book, you should have plenty of ideas about where to continue your journey into software architecture.
Here are a few pointers about this book that are worth mentioning:
The chapters are organized to start with small-scale patterns and then progress to higher-level ones, making the learning curve easier.
Instead of giving you a recipe, the book focuses on the thinking behind things and shows the evolution of some techniques to help you understand why the shift happened.
Many use cases combine more than one design pattern to illustrate alternate usage so you can understand and use the patterns efficiently. This also shows that design patterns are not beasts to tame but tools to use, manipulate, and bend to your will.
As in real life, no textbook solution can solve all our problems; real problems are always more complicated than what’s explained in textbooks. In this book, I aim to show you how to mix and match patterns to think “architecture” instead of giving you step-by-step instructions to reproduce.
The rest of the introduction chapter introduces the concepts we explore throughout the book, including refreshers on a few notions. We also touch on .NET, its tooling, and some technical requirements.In this chapter, we cover the following topics:
What is a design pattern?
Anti-patterns and code smell. Understanding the web – request/response. Getting started with .NET.
What is a design pattern?
Since you just purchased a book about design patterns, I guess you have some idea of what design patterns are, but let’s make sure that we are on the same page.Abstract definition: A design pattern is a proven technique that we can use to solve a specific problem.In this book, we apply different patterns to solve various problems and leverage some open-source tools to go further, faster! Abstract definitions make people sound smart, but understanding concepts requires more practice, and there is no better way to learn than by experimenting with something, and design patterns are no different.If that definition does not make sense to you yet, don’t worry. You should have enough information by the end of the book to correlate the multiple practical examples and explanations with that definition, making it crystal clear.I like to compare programming to playing with LEGO® because what you have to do is very similar: put small pieces together to create something bigger. Therefore, if you lack imagination or skills, possibly because you are too young, your castle might not look as good as someone with more experience. With that analogy in mind, a design pattern is a plan to assemble a solution that fits one or more scenarios, like the tower of a castle. Once you designed a single tower, you can build multiple by following the same steps. Design patterns act as that tower plan and give you the tools to assemble reliable pieces to improve your masterpiece (program).However, instead of snapping LEGO® blocks together, you nest code blocks and interweave objects in a virtual environment!Before going into more detail, well-thought-out applications of design patterns should improve your application designs. That is true whether designing a small component or a whole system. However, be careful: throwing patterns into the mix just to use them can lead to the opposite result: over-engineering. Instead, aim to write the least amount of readable code that solves your issue or automates your process.As we have briefly mentioned, design patterns apply to different software engineering levels, and in this book, we start small and grow to a cloud-scale! We follow a smooth learning curve, starting with simpler patterns and code samples that bend good practices to focus on the patterns—finally ending with more advanced topics and good practices.Of course, some subjects are overviews more than deep dives, like automated testing, because no one can fit it all in a single book. Nonetheless, I’ve done my best to give you as much information about architecture-related subjects as possible to ensure the proper foundations are in place for you to get as much as possible out of the more advanced topics, and I sincerely hope you’ll find this book a helpful and enjoyable read.Let’s start with the opposite of design patterns because it is essential to identify wrong ways of doing things to avoid making those mistakes or to correct them when you see them. Of course, knowing the right way to overcome specific problems using design patterns is also crucial.
Anti-patterns and code smells
Anti-patterns and code smells are bad architectural practices or tips about possible bad design. Learning about best practices is as important as learning about bad ones, which is where we start. The book highlights multiple anti-patterns and code smells to help you get started. Next, we briefly explore the first few.
Anti-patterns
An anti-pattern is the opposite of a design pattern: it is a proven flawed technique that will most likely cause you trouble and cost you time and money (and probably give you headaches).An anti-
pattern is a pattern that seems a good idea and seems to be the solution you were looking for, but it causes more harm than good. Some anti-patterns started as legitimate design patterns and were labelled anti-patterns later. Sometimes, it is a matter of opinion, and sometimes the classification can be influenced by the programming language or technologies.Let’s look at an example next. We will explore some other anti-patterns throughout the book.
Anti-pattern – God Class
A God class is a class that handles too many things. Typically, this class serves as a central entity which many other classes inherit or use within the application it is the class that knows and manages everything in the system; it is the class. On the other hand, it is also the class that nobody wants to update, which breaks the application every time somebody touches it: it is an evil class!The best way to fix this is to segregate responsibilities and allocate them to multiple classes rather than concentrating them in a single class. We look at how to split responsibilities throughout the book, which helps create more robust software.If you have a personal project with a God class at its core, start by reading the book and then try to apply the principles and patterns you learn to divide that class into multiple smaller classes that interact together. Try to organize those new classes into cohesive units, modules, or assemblies.To help fix God classes, we dive into architectural principles in Chapter 3, Architectural Principles, opening the way to concepts such as responsibility segregation.
Code smells
A code smell is an indicator of a possible problem. It points to areas of your design that could benefit from a redesign. By “code smell,” we mean “code that stinks” or “code that does not smell right.”It is important to note that a code smell only indicates the possibility of a problem; it does not mean a problem exists. Code smells are usually good indicators, so it is worth analyzing your software’s “smelly” parts.An excellent example is when a method requires many comments to explain its logic. That often means that the code could be split into smaller methods with proper names, leading to more readable code and allowing you to get rid of those pesky comments.Another note about comments is that they don’t evolve, so what often happens is that the code described by a comment changes, but the comment remains the same. That leaves a false or obsolete description of a block of code that can lead a developer astray.The same is also true with method names. Sometimes, the method’s name and body tell a different story, leading to the same issues. Nevertheless, this happens less often than orphan or obsolete comments since programmers tend to read and write code better than spoken language comments. Nonetheless, keep that in mind when reading, writing, or reviewing code.
Code smell – Control Freak
An excellent example of a code smell is using the new keyword. This indicates a hardcoded dependency where the creator controls the new object and its lifetime. This is also known as the Control Freak anti-pattern, but I prefer to box it as a code smell instead of an anti-pattern since the new keyword is not intrinsically wrong.At this point, you may be wondering how it is possible not to use the new keyword in object-oriented programming, but rest assured, we will cover that and expand on the control freak code smell in Chapter 7, Deep Dive into Dependency Injection.
Code smell – Long Methods
The long methods code smell is when a method extends to more than 10 to 15 lines of code. That is a good indicator that you should think about that method differently. Having comments that separate multiple code blocks is a good indicator of a method that may be too long.Here are a few examples of what the case might be:
The method contains complex logic intertwined in multiple conditional statements.
The method contains a big switch block. The method does too many things. The method contains duplications of code.
To fix this, you could do the following:
Extract one or more private methods.
Extract some code to new classes.
Reuse the code from external classes.
If you have a lot of conditional statements or a huge switch block, you could leverage a design pattern such as the Chain of Responsibility, or CQRS, which you will learn about in Chapter 10, Behavioral Patterns, and Chapter 14, Mediator and CQRS Design Patterns.
Usually, each problem has one or more solutions; you need to spot the problem and then find, choose, and implement one of the solutions. Let’s be clear: a method containing 16 lines does not necessarily need refactoring; it could be OK. Remember that a code smell indicates that there might be a problem, not that there necessarily is one—apply common sense.
Understanding the web – request/response
Before going any further, it is imperative to understand the basic concept of the web. The idea behind HTTP 1.X is that a client sends an HTTP request to a server, and then the server responds to that client. That can sound trivial if you have web development experience. However, it is one of the most important web programming concepts, irrespective of whether you are building web APIs, websites, or complex cloud applications.Let’s reduce an HTTP request lifetime to the following:
1. The communication starts.
2 The client sends a request to the server.
3. The server receives the request.
4. The server does something with the request, like executing code/logic.
5. The server responds to the client.
6. The communication ends.
After that cycle, the server is no longer aware of the client. Moreover, if the client sends another request, the server is unaware that it responded to a request earlier for that same client because HTTP is stateless.There are mechanisms for creating a sense of persistence between requests for the server to be “aware” of its clients. The most well-known of these is cookies.If we dig deeper, an HTTP request comprises a header and an optional body. Then, requests are sent using a specific method. The most common HTTP methods are GET and POST On top of those, extensively used by web APIs, we can add PUT , DELETE , and PATCH to that list.Although not every HTTP method accepts a body, can respond with a body, or should be idempotent, here is a quick reference table:
MethodRequest has bodyResponse has body Idempotent
GET No* Yes Yes
POST Yes Yes No
PUT Yes No Yes
PATCH Yes Yes No
DELETE May May Yes
* Sending a body with a GET request is not forbidden by the HTTP specifications, but the semantics of such a request are not defined either. It is best to avoid sending GET requests with a body.
An idempotent request is a request that always yields the same result, whether it is sent once or multiple times. For example, sending the same POST request multiple times should create multiple
similar entities, while sending the same DELETE request multiple times should delete a single entity. The status code of an idempotent request may vary, but the server state should remain the same. We explore those concepts in more depth in Chapter 4, Model-View-Controller.Here is an example of a GET request:
GET http: //www.forevolve.com/ HTTP/1.1
Host: www.forevolve.com
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,fr-CA;q=0.8,fr;q=0.7
Cookie: ...
The HTTP header comprises a list of key/value pairs representing metadata that a client wants to send to the server. In this case, I queried my blog using the GET method and Google Chrome attached some additional information to the request. I replaced the Cookie header’s value with because it can be pretty large and that information is irrelevant to this sample. Nonetheless, cookies are passed back and forth like any other HTTP header.
Important note about cookies
The client sends cookies, and the server returns them for every request-response cycle. This could kill your bandwidth or slow down your application if you pass too much information back and forth (cookies or otherwise). One good example would be a serialized identity cookie that is very large.
Another example, unrelated to cookies but that created such a back-and-forth, was the good old Web Forms ViewState . This was a hidden field sent with every request. That field could become very large when left unchecked.
Nowadays, with high-speed internet, it is easy to forget about those issues, but they can significantly impact the user experience of someone on a slow network.
When the server decides to respond to the request, it returns a header and an optional body, following the same principles as the request. The first line indicates the request’s status: whether it was successful. In our case, the status code was 200 , which indicates success. Each server can add more or less information to its response. You can also customize the response with code.Here is the response to the previous request:
HTTP/1.1 200 OK
Server: GitHub.com
Content-Type: text/html; charset=utf-8
Last-Modified: Wed, 03 Oct 2018 21:35:40 GMT
ETag: W/"5bb5362c-f677"
Access-Control-Allow-Origin: *
Expires: Fri, 07 Dec 2018 02:11:07 GMT
Cache-Control: max-age=600
Content-Encoding: gzip
X-GitHub-Request-Id: 32CE:1953:F1022C:1350142:5C09D460
Content-Length: 10055
Accept-Ranges: bytes
Date: Fri, 07 Dec 2018 02:42:05 GMT
Via: 1.1 varnish
Age: 35
Connection: keep-alive
X-Served-By: cache-ord1737-ORD
X-Cache: HIT
X-Cache-Hits: 2
X-Timer: S1544150525.288285,VS0,VE0
Vary: Accept-Encoding
X-Fastly-Request-ID: 98a36fb1b5642c8041b88ceace73f25caaf07746
<Response body truncated for brevity>
Now that the browser has received the server’s response, it renders the HTML webpage. Then, for each resource, it sends another HTTP call to its URI and loads it. A resource is an external asset, such as an image, a JavaScript file, a CSS file, or a font.After the response, the server is no longer aware of the client; the communication has ended. It is essential to understand that to create a pseudo-state between each request, we need to use an external mechanism. That mechanism could be the session-state leveraging cookies, simply using cookies, or some other ASP.NET Core mechanisms, or we could create a stateless application. I recommend going stateless whenever possible. We write primarily stateless applications in the book.
Note
If you want to learn more about session and state management, I left a link in the Further reading section at the end of the chapter.
As you can imagine, the backbone of the internet is its networking stack. The Hypertext Transfer Protocol (HTTP) is the highest layer of that stack (layer 7). HTTP is an application layer built on the Transmission Control Protocol (TCP). TCP (layer 4) is the transport layer, which defines how data is moved over the network (for instance, the transmission of data, the amount of transmitted data, and error checking). TCP uses the Internet Protocol (IP) layer to reach the computer it tries to talk to. IP (layer 3) represents the network layer, which handles packet IP addressing.A packet is a chunk of data that is transmitted over the wire. We could send a large file directly from a source to a destination machine, but that is not practical, so the network stack breaks down large items into smaller packets. For example, the source machine breaks a file into multiple packets, sends them to the target machine, and then the target reassembles them back into the source file. This process allows numerous senders to use the same wire instead of waiting for the first transmission to be done. If a packet gets lost in transit, the source machine can also send only that packet back to the target machine.Rest assured, you don’t need to understand every detail behind networking to program web applications, but it is always good to know that HTTP uses TCP/IP and chunks big payloads into smaller packets. Moreover, HTTP/1 limits the number of parallel requests a browser can open simultaneously. This knowledge can help you optimize your apps. For example, a high number of assets to load, their size, and the order in which they are sent to the browser can increase the page load time, the perceived page load time, or the paint time.To conclude this subject and not dig too deep into networking, HTTP/1 is older but foundational. HTTP/2 is more efficient and supports streaming multiple assets using the same TCP connection. It also allows the server to send assets to the client before it requests the resources, called a server push.If you find HTTP interesting, HTTP/2 is an excellent place to start digging deeper, as well as the HTTP/3 proposed standard that uses the QUIC transport protocol instead of HTTP (RFC 9114). ASP.NET Core 7.0+ supports HTTP/3, which is enabled by default in ASP.NET Core 8.0.Next, let’s quickly explore .NET.
Getting started with NET
A bit of history: .NET Framework 1.0 was first released in 2002. .NET is a managed framework that compiles your code into an Intermediate Language (IL) named Microsoft Intermediate Language (MSIL). That IL code is then compiled into native code and executed by the Common Language Runtime (CLR). The CLR is now known simply as the .NET runtime After releasing several versions of .NET Framework, Microsoft never delivered on the promise of an interoperable stack. Moreover, many flaws were built into the core of .NET Framework, tying it to Windows.Mono, an open-source project, was developed by the community to enable .NET code to run on nonWindows OSes. Mono was used and supported by Xamarin, acquired by Microsoft in 2016. Mono enabled .NET code to run on other OSes like Android and iOS. Later, Microsoft started to develop an official cross-platform .NET SDK and runtime they named .NET Core.The .NET team did a magnificent job building ASP.NET Core from the ground up, cutting out compatibility with the older .NET Framework versions. That brought its share of problems at first, but .NET Standard alleviated
the interoperability issues between the old .NET and the new .NET.After years of improvements and two major versions in parallel (Core and Framework), Microsoft reunified most .NET technologies into .NET 5+ and the promise of a shared Base Class Library (BCL). With .NET 5, .NET Core simply became .NET while ASP.NET Core remained ASP.NET Core. There is no .NET “Core” 4, to avoid any potential confusion with .NET Framework 4.X.New major versions of .NET release every year now. Even-number releases are Long-Term Support (LTS) releases with free support for 3 years, and odd-number releases (Current) have free support for only 18 months.The good thing behind this book is that the architectural principles and design patterns covered should remain relevant in the future and are not tightly coupled with the versions of .NET you are using. Minor changes to the code samples should be enough to migrate your knowledge and code to new versions.Next, let’s cover some key information about the .NET ecosystem.
.NET SDK versus runtime
You can install different binaries grouped under SDKs and runtimes. The SDK allows you to build and run .NET programs, while the runtime only allows you to run .NET programs.As a developer, you want to install the SDK on your deployment environment. On the server, you want to install the runtime. The runtime is lighter, while the SDK contains more tools, including the runtime.
.NET 5+ versus .NET Standard
When building .NET projects, there are multiple types of projects, but basically, we can separate them into two categories:
Applications Libraries
Applications target a version of .NET, such as net5.0 and net6.0 . Examples of that would be an ASP.NET application or a console application.Libraries are bundles of code compiled together, often distributed as a NuGet package. .NET Standard class library projects allow sharing code between .NET 5+, and .NET Framework projects. .NET Standard came into play to bridge the compatibility gap between .NET Core and .NET Framework, which eased the transition. Things were not easy when .NET Core 1.0 first came out.With .NET 5 unifying all the platforms and becoming the future of the unified .NET ecosystem, .NET Standard is no longer needed. Moreover, app and library authors should target the base Target Framework Moniker (TFM), for example, net8.0 . You can also target netstandard2.0 or netstandard2.1 when needed, for example, to share code with .NET Framework. Microsoft also introduced OS-specific TFMs with .NET 5+, allowing code to use OSspecific APIs like net8.0-android and net8.0-tvos . You can also target multiple TFMs when needed.
Note
I’m sure we will see .NET Standard libraries stick around for a while. All projects will not just migrate from .NET Framework to .NET 5+ magically, and people will want to continue sharing code between the two.
The next versions of .NET are built over .NET 5+, while .NET Framework 4.X will stay where it is today, receiving only security patches and minor updates. For example, .NET 8 is built over .NET 7, iterating over .NET 6 and 5.Next, let’s look at some tools and code editors.
Visual Studio Code versus Visual Studio versus the command-line interface
How can one of these projects be created? .NET Core comes with the dotnet command-line interface (CLI), which exposes multiple commands, including new . Running the dotnet new command in a terminal generates a new project.To create an empty class library, we can run the following commands:
md MyProject
cd MyProject
dotnet new classlib
That would generate an empty class library in the newly created MyProject directory.The -h option helps discover available commands and their options. For example, you can use dotnet -h to find the available SDK commands or dotnet new -h to find out about options and available templates.It is fantastic that .NET now has the dotnet CLI. The CLI enables us to automate our workflows in continuous integration (CI) pipelines while developing locally or through any other process.The CLI also makes it easier to write documentation that anyone can follow; writing a few commands in a terminal is way easier and faster than installing programs like Visual Studio and emulators.Visual Studio Code is my favourite text editor. I don’t use it much for .NET coding, but I still do to reorganize projects, when it’s CLI time, or for any other task that is easier to complete using a text editor, such as writing documentation using Markdown, writing JavaScript or TypeScript, or managing JSON, YAML, or XML files. To create a C# project, a Visual Studio solution, or to add a NuGet package using Visual Studio Code, open a terminal and use the CLI.As for Visual Studio, my favourite C# IDE, it uses the CLI under the hood to create the same projects, making it consistent between tools and just adding a user interface on top of the dotnet new CLI command.You can create and install additional dotnet new project templates in the CLI or even create global tools. You can also use another code editor or IDE if you prefer. Those topics are beyond the scope of this book.
An overview of project templates
Here is an example of the templates that are installed ( dotnet new --list ):
A study of all the templates is beyond the scope of this book, but I’d like to visit the few that are worth mentioning, some of which we will use later:
dotnet new console creates a console application
dotnet new classlib creates a class library
dotnet new xunit creates an xUnit test project
dotnet new web creates an empty web project
dotnet new mvc scaffolds an MVC application
dotnet new webapi scaffolds a web API application
Running and building your program
Figure 1.1: Project templates
If you are using Visual Studio, you can always hit the play button, or F5, and run your app. If you are using the CLI, you can use one of the following commands (and more). Each of them also offers different options to control their behaviour. Add the -h flag with any command to get help on that command, such as dotnet build -h :
Command Description
dotnet restore
Restore the dependencies (a.k.a. NuGet packages) based on the .csproj or .sln file present in the current dictionary.
dotnet build Build the application based on the .csproj or .sln file present in the current dictionary. It implicitly runs the restore command first.
dotnet run Run the current application based on the .csproj file present in the current dictionary. It implicitly runs the build and restore commands first.
dotnet watch run Watch for file changes. When a file has changed, the CLI updates the code from that file using the hot-reload feature. When that is impossible, it rebuilds the application and then reruns it (equivalent to executing the run command again). If it is a web application, the page should refresh automatically.
dotnet test Run the tests based on the .csproj or .sln file present in the current directory. It implicitly runs the build and restore commands first. We cover testing in the next chapter.
dotnet watch test Watch for file changes. When a file has changed, the CLI reruns the tests (equivalent to executing the test command again).
dotnet publish Publish the current application, based on the .csproj or .sln file present in the current directory, to a directory or remote location, such as a hosting provider. It implicitly runs the build and restore commands first.
dotnet pack Create a NuGet package based on the .csproj or .sln file present in the current directory. It implicitly runs the build and restore commands first. You don’t need a .nuspec file.
dotnet clean Clean the build(s) output of a project or solution based on the .csproj or .sln file present in the current directory.
Technical requirements
Throughout the book, we will explore and write code. I recommend installing Visual Studio, Visual Studio Code, or both to help with that. I use Visual Studio and Visual Studio Code. Other alternatives are Visual Studio for Mac, Riders, or any other text editor you choose.Unless you install Visual Studio, which comes with the .NET SDK, you may need to install it. The SDK comes with the CLI we explored earlier and the build tools for running and testing your programs. Look at the README.md file in the GitHub repository for more information and links to those resources.The source code of all chapters is available for download on GitHub at the following address: https://adpg.link/net6
Summary
This chapter looked at design patterns, anti-patterns, and code smells. We also explored a few of them. We then moved on to a recap of a typical web application’s request/response cycle.We continued by exploring .NET essentials, such as SDK versus runtime and app targets versus .NET Standard. We then dug a little more into the .NET CLI, where I laid down a list of essential commands, including dotnet build and dotnet watch run . We also covered how to create new projects. This has set us up to explore the different possibilities we have when building our .NET applications.In the next two chapters, we explore automated testing and architectural principles. These are foundational chapters for building robust, flexible, and maintainable applications.
Questions
Let’s take a look at a few practice questions:
1. Can we add a body to a GET request?
2. Why are long methods a code smell?
3. Is it true that .NET Standard should be your default target when creating libraries?
4. What is a code smell?
Further reading
Here are some links to consolidate what has been learned in the chapter:
Overview of how .NET is versioned: https://adpg.link/n52L .NET CLI overview: https://adpg.link/Lzx3
Custom templates for dotnet new : https://adpg.link/74i2
Session and state management in ASP.NET Core: https://adpg.link/Xzgf