Architecting ASP.NET Core Applications - Carl-Hugo Marcotte - E-Book

Architecting ASP.NET Core Applications E-Book

Carl-Hugo Marcotte

0,0
34,79 €

-100%
Sammeln Sie Punkte in unserem Gutscheinprogramm und kaufen Sie E-Books und Hörbücher mit bis zu 100% Rabatt.
Mehr erfahren.
Beschreibung

This unique ASP.NET Core book will fill in the gaps in your REST API and backend designs. Learn how to build robust, maintainable, and flexible apps using Gang of Four (GoF) design patterns and modern architectural principles. This new edition is updated for .NET 8 and focuses exclusively on the backend, with new content on REST APIs, the REPR pattern, and building modular monoliths.

You’ll start by covering foundational concepts like REST, the SOLID principles, Minimal APIs, dependency injection in .NET, and other ASP.NET Core 8 mechanisms. Then, you’ll learn to develop components using design patterns, including many from the GoF. Finally, you’ll explore organizing your application code with patterns that vary from layers to feature-oriented vertical slice designs, covering CQS and a deep dive into microservices along the way. A brand-new e-commerce project at the end of the book will tie it all together.

This how-to guide will teach you how to assemble your own APIs from building blocks, to suit whatever real-world requirements you may have.

Das E-Book können Sie in Legimi-Apps oder einer beliebigen App lesen, die das folgende Format unterstützen:

EPUB
MOBI

Seitenzahl: 1017

Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Architecting ASP.NET Core Applications

Third Edition

An atypical design patterns guide for .NET 8, C# 12, and beyond

Carl-Hugo Marcotte

BIRMINGHAM—MUMBAI

Architecting ASP.NET Core Applications

Third Edition

Copyright © 2024 Packt Publishing

All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.

Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing or its dealers and distributors, will be held liable for any damages caused or alleged to have been caused directly or indirectly by this book.

Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.

Publishing Product Manager: Lucy Wan

Acquisition Editor – Peer Reviews: Gaurav Gavas

Project Editor: Rianna Rodrigues

Content Development Editor: Rebecca Youé

Copy Editor: Safis Editor

Technical Editor: Srishty Bhardwaj

Proofreader: Safis Editor

Indexer: Rekha Nair

Presentation Designer: Rajesh Shirsath

Senior Developer Relations Marketing Executive: Priyadarshini Sharma

First published: December 2020

Second published: March 2022

Third edition: March 2024

Production reference: 1150324

Published by Packt Publishing Ltd.

Grosvenor House

11 St Paul’s Square

Birmingham

B3 1RB, UK.

ISBN 978-1-80512-338-5

www.packt.com

Foreword

I’ve been programming for over 20 years now. Some of those years were spent as a kid trying to build stuff for fun, but for the majority of that time I’ve been building software professionally. C# has been my go-to when building software systems, and I’ve been fortunate to work in organizations that fully embrace it (including Microsoft!).

Early on in my professional career I became a software engineering manager, and I was able to shift my efforts toward helping other software engineers become better at their craft. I’ve spent years working with software developers, helping to keep them engaged in their work and their learning. It’s my personal mission to ensure the barrier to becoming an awesome software engineer is brought down, and C# and .NET have been excellent programming tools to assist in that journey.

Carl-Hugo Marcotte’s Architecting ASP.NET Core Applications is fully in line with my preferred way to teach others software engineering concepts. The content in the book is organized in a way that gradually builds on programming principles as demonstrated in C#, giving readers some theory that is quickly followed by code examples. From there, learning is solidified with mini projects where readers can apply their theory immediately to something practical.

Readers that work through the content in this book should expect to build confidence in design and architectural patterns in ASP.NET Core. This will allow for building more robust and scalable systems. Having tangible and concrete code examples to refer back to every step of the way through this book is one of its greatest strengths.

The design patterns are gradually introduced with explanations regarding their purpose and guidelines for implementations. From there, they’re given a practical application in ASP.NET Core. The visuals coupled with hands-on code examples and projects all play together to reinforce learning from different angles. Seeing concepts directly applied in realistic scenarios helps reinforce the pros and cons of each.

Additionally, testing approaches are built into the content of this book at every opportunity. This is a refreshing take on software engineering material where there’s typically a hyper-focus on design, performance, and computer science. However, Carl-Hugo Marcotte’s examples are backed by excellent testing examples that highlight how to validate the different concepts being implemented.

For software developers reading this book who are newer to developing ASP.NET Core applications, I hope that you feel more confident in your skillset by the end. For those readers that have more experience developing web applications in C#, I believe that this book will help to reinforce good design decisions and offer you some alternative perspectives on things you are already familiar with. Regardless of your prior skill level, by the end of this book, you will be positioned to build better applications.

Nick Cosentino, Principal Software Engineering Manager at Microsoft

Contributors

About the author

Carl-Hugo Marcotte is a software craftsman who has developed digital products professionally since 2005; his coding journey started around 1989 for fun. He has a bachelor’s degree in computer science. He has acquired a solid background in software architecture and expertise in ASP.NET Core through creating a wide range of web and cloud applications, from custom e-commerce websites to enterprise applications. He served many customers as an independent consultant, taught programming, and is now a Principal Architect at Export Development Canada. Passionate about C#, ASP.NET Core, AI, automation, and cloud computing, he fosters collaboration and the open-source ethos, sharing his expertise with the tech community.

I want to thank everyone who has supported me throughout my career, from my mom, who bought my first computers when I was a kid, to my other half and partner in life, Cathie, who is always there no matter the idea I pursue.

About the reviewer

Davide Bellone is a backend developer with over 10 years of professional experience. Based in Italy, he’s worked with Microsoft platforms and tools since 2014. He has worked with .NET Core, SQL, Azure, and more. He loves learning new things and thinks the best way to learn is to share; that’s why he started his journey as a content creator and conference speaker. He’s the author of Code4IT.dev, a blog for C# and Azure developers. He’s honoured to be a Microsoft MVP in Developer Technologies.

About the beta reader

Kevin Viera is a software engineer passionate about software architecture and emerging technologies. He has a diverse background covering various aspects of web development and computer science. Kevin’s trajectory led him through a spectrum of languages and technologies such as C++, C#, JavaScript, and Python, before he began specializing in the .NET environment.

Throughout his career, Kevin has shown a strong interest in delving into the complexities of the .NET ecosystem. He began with the Windows-only .NET Framework, progressed to the cross-platform .NET Core, and finally focused on the current .NET environment as his main career path.

Learn more on Discord

To join the Discord community for this book – where you can share feedback, ask questions to the author, and learn about new releases – follow the QR code below:

https://packt.link/ArchitectingASPNETCoreApps3e

Contents

Preface

Who this book is for

What this book covers

To get the most out of this book

Get in touch

Section 1: Principles and Methodologies

Introduction

What is a design pattern?

Anti-patterns and code smells

Anti-patterns

Anti-pattern: God class

Code smells

Code smell: Control Freak

Code smell: long methods

Understanding the web: request/response

Getting started with .NET

.NET SDK versus runtime

.NET 5+ versus .NET Standard

The command-line interface versus Visual Studio Code versus Visual Studio

An overview of project templates

Running and building your program

Technical requirements

Summary

Questions

Further reading

Answers

Automated Testing

An overview of automated testing

Unit testing

Integration testing

End-to-end testing

Other types of tests

Finding the right balance

Enhancing code quality

Improving quality with refactoring

Managing technical debt

Testing approaches

TDD

ATDD

BDD

Testing techniques

White-box testing

Black-box testing

Gray-box testing

White-box vs. black-box vs. gray-box testing

Conclusion

Test case creation

Equivalence partitioning

Boundary value analysis

Decision table testing

State transition testing

Use case testing

Introducing the xUnit framework

How to create an xUnit test project

Key xUnit features

FactAttribute

Assertions

TheoryAttribute

Fixture

Arrange, Act, Assert

Organizing your tests

Unit tests

Namespaces

Test class names

Test code inside the test class

Integration tests

Writing ASP.NET Core integration tests

Classic web application

Minimal hosting

Workaround

Alternative to using fixtures

Creating a reusable test application

Important testing principles

Summary

Questions

Further reading

Answers

Architectural Principles

Separation of concerns (SoC)

Don’t repeat yourself (DRY)

Keep it simple, stupid (KISS)

You Aren’t Gonna Need It (YAGNI)

The SOLID principles

Single responsibility principle (SRP)

Project – Single Responsibility

Open/closed principle (OCP)

Project – Open Close

Liskov substitution principle (LSP)

The LSP explained

Covariance and contravariance

Project – Liskov Substitution

Conclusion

Interface segregation principle (ISP)

What is an interface?

Project – Interface Segregation

Conclusion

Dependency inversion principle (DIP)

Direct dependency

Inverted dependency

Direct subsystems dependency

Inverted subsystems dependency

Project – Dependency inversion

Conclusion

Summary

Questions

Further reading

Answers

REST APIs

REST and HTTP

HTTP methods

HTTP status codes

HTTP headers

Versioning

Default versioning strategy

Versioning strategy

Wrapping up

The Data Transfer Object (DTO) pattern

Goal

Design

Conceptual examples

Registering for an activity

Fetching activity registration details

Conclusion

API contracts

Code-first API contract

The first endpoint

The second endpoint

Wrapping up

Summary

Questions

Further reading

Answers

Section 2: Designing with ASP.NET Core

Minimal APIs

Top-level statements

Minimal hosting

Minimal APIs

Mapping a route to a delegate

Configuring endpoints

Inputs

Outputs

Metadata

Configuring JSON serialization

Leveraging endpoint filters

Leveraging the endpoint filter factory

Organizing endpoints

MapGroup

Creating a custom Map extension method

Class libraries

Using Minimal APIs with Data Transfer Objects

Goal

Design

Project – Minimal API

Raw CRUD endpoints

DTO-enabled endpoints

Conclusion

Summary

Questions

Further reading

Answers

Model-View-Controller

The MVC design pattern

Goal

Design

Anatomy of ASP.NET Core web APIs

The entry point

Directory structure

Controller

Returning values

Attribute routing

Conclusion

Using MVC with DTOs

Goal

Design

Project – MVC API

Raw CRUD controller

DTO controller

Conclusion

Summary

Questions

Further reading

Answers

Strategy, Abstract Factory, and Singleton Design Patterns

The Strategy design pattern

Goal

Design

Project – Strategy

Conclusion

The Abstract Factory design pattern

Goal

Design

Project – Abstract Factory

Project – the mid-range vehicle factory

Impacts of the Abstract Factory

Conclusion

The Singleton design pattern

Goal

Design

An alternative (better) way

Code smell – Ambient Context

Conclusion

Summary

Questions

Answers

Dependency Injection

What is dependency injection?

The composition root

Striving for adaptability

Understanding the use of the IoC container

The role of an IoC container

Code smell – Control Freak

Stable dependencies

Volatile dependencies

Conclusion

Object lifetime

What’s an object’s lifetime?

.NET object lifetime

Registering our dependencies

Registering your features elegantly

Project – Registering the demo feature

Using external IoC containers

Revisiting the Strategy pattern

Constructor injection

Property injection

Method injection

Project – Strategy

Shared building blocks

Control Freak controllers

Injecting an implementation in the controllers

Injecting an abstraction in the controller

Constructing the InjectAbstractionLocationsController

Conclusion

Revisiting the Singleton pattern

Project – Application state

First implementation

Second implementation

Using the implementations

Project – Wishlist

Conclusion

Understanding guard clauses

Understanding the Service Locator pattern

Project – ServiceLocator

Implementing method injection

Implementing constructor injection

Implementing a minimal API

Conclusion

Revisiting the Factory pattern

Project – Factory

Summary

Questions

Further reading

Answers

Application Configuration and the Options Pattern

Loading the configuration

Learning the options interfaces

IOptionsMonitor<TOptions>

IOptionsFactory<TOptions>

IOptionsSnapshot<TOptions>

IOptions<TOptions>

Exploring common usage scenarios

Manual configuration

Using the settings file

Injecting options

Named options

IOptionsFactory<MyOptions>

IOptionsMonitor<MyOptions>

IOptionsSnapshot<MyOptions>

Bind options to an existing object

Reloading options at runtime

Learning options configuration

Creating the program

Configuring the options

Implementing a configurator object

Adding post-configuration

Using multiple configurator objects

Exploring other configuration possibilities

Validating our options objects

Eager validation

Data annotations

Validation types

Validating options using FluentValidation

Injecting options objects directly

Centralizing the configuration for easier management

Using the configuration-binding source generator

Using the options validation source generator

Using the ValidateOptionsResultBuilder class

Wrapping up

Summary

Questions

Further reading

Answers

Logging Patterns

What is logging?

Writing logs

Log levels

Logging providers

Configuring logging

Structured logging

Summary

Questions

Further reading

Answers

Section 3: Component Patterns

Structural Patterns

The Decorator design pattern

Goal

Design

Project – Adding behaviors

DecoratorA

DecoratorB

Project – Decorator using Scrutor

Conclusion

The Composite design pattern

Goal

Design

Project – BookStore

Conclusion

The Adapter design pattern

Goal

Design

Project – Greeter

Conclusion

The Façade design pattern

Goal

Design

Project – The façades

Opaque façade

Transparent façade

The program

Flexibility in action

Alternative façade patterns

Conclusion

Summary

Questions

Further reading

Answers

Behavioral Patterns

The Template Method pattern

Goal

Design

Project – Building a search machine

Unit tests

Conclusion

The Chain of Responsibility pattern

Goal

Design

Project – Message interpreter

Conclusion

Mixing the Template Method and Chain of Responsibility patterns

Project – Improved message interpreter

Project – A final, finer-grained design

Conclusion

Summary

Questions

Answers

Operation Result Pattern

The Operation Result pattern

Goal

Design

Project – Implementing different Operation Result patterns

The Program.cs file

The simplest form of the Operation Result pattern

A single error message

Adding a return value

Multiple error messages

Adding message severity

Sub-classes and factories

Project – Registration Application

Advantages and disadvantages

Advantages

Disadvantages

Conclusion

Summary

Questions

Further reading

Answers

Section 4: Application Patterns

Anti-pattern – Big Ball of Mud

Layering and Clean Architecture

Introducing layering

Classic layering model

Splitting the layers

Layers versus tiers versus assemblies

What is a tier?

What is a layer?

What is an assembly?

Responsibilities of the common layers

Presentation

Domain

Rich domain model

Anemic domain model

The Service layer

Data

Overview of the Repository pattern

Overview of the Unit of Work pattern

Abstract layers

Sharing the model

Clean Architecture

Implementing layering in real life

To be or not to be a purist?

Building a façade over a database

Summary

Questions

Further reading

Answers

Object Mappers

The Object Mapper pattern

Goal

Design

Project – Mapper

Conclusion

Code smell – too many dependencies

Overview of the Aggregate Services pattern

Implementing a mapping façade

Implementing a mapping service

Exploring AutoMapper

Project – AutoMapper

Exploring Mapperly

Project – Mapperly

Summary

Questions

Further reading

Answers

Mediator and CQS Patterns

A high-level overview of Vertical Slice Architecture

The Mediator pattern

Goal

Design

Project – Mediator (IMediator)

Project – Mediator (IChatRoom)

Conclusion

The CQS pattern

Goal

Design

Project – CQS

Conclusion

Code smell – Marker Interfaces

Metadata

Dependency identifier

Using MediatR as a mediator

Project – Clean Architecture with MediatR

Summary

Questions

Further reading

Answers

Getting Started with Vertical Slice Architecture

Vertical Slice Architecture

What are the advantages and disadvantages?

Advantages

Disadvantages

Downsides or upsides?

Project – Vertical Slice Architecture

Project organization

Exploring a feature

Testing

Continuing your journey – a few tips and tricks

Agile and DevOps synergy

Conclusion

Summary

Questions

Further reading

Answers

Request-EndPoint-Response (REPR)

The Request-EndPoint-Response (REPR) pattern

Goal

Design

Project – SimpleEndpoint

Feature: ShuffleText

Feature: RandomNumber

Feature: UpperCase

Conclusion

An e-commerce application—a slice of the real-world

Assembling our stack

Dissecting the code structure

Exploring the shopping basket

AddItem feature

Managing exception handling

Creating an exception handler middleware

Exception handling using ExceptionMapper

Leveraging exceptions to propagate errors

Gray-box testing

AddItemTest

Summary

Questions

Further reading

Answers

Introduction to Microservices Architecture

What are microservices?

Cohesive unit of business

Ownership of data

Microservice independence

An introduction to Event-Driven Architecture

Types of events

Domain events

Integration events

Application events

Enterprise events

Getting started with message queues

Overview of the Publish-Subscribe pattern

Message brokers

Overview of the Event Sourcing pattern

Example

Conclusion

Introducing Gateway patterns

Overview of the Gateway Routing pattern

Overview of the Gateway Aggregation pattern

Overview of the Backend for Frontend pattern

Mixing and matching gateways

Conclusion

Project – BFF

Layering APIs

Advantages of a layered API design

Disadvantages of a layered API design

Running the microservices

Manually starting the projects

Using Docker Compose to run the projects

Briefly testing the services

Creating typed HTTP clients using Refit

Creating a service that serves the current customer

Features

Fetching the catalog

Fetching the shopping cart

Managing the shopping cart

Conclusion

Revisiting the CQRS pattern

Advantages and potential risks

Benefits of the CQRS pattern

Potential risks of using the CQRS pattern

Conclusion

Overview of the Microservice Adapter pattern

Adapting an existing system to another

Decommissioning a legacy application

Adapting an event broker to another

Conclusion

Summary

Questions

Further reading

Answers

Modular Monolith

What is a Modular Monolith?

What are traditional monoliths?

What are microservices?

Advantages of Modular Monoliths

Key components of a Modular Monolith

Implementing a Modular Monolith

Planning the project

Analyzing the domain

Identifying the modules

Identifying the interactions between modules

Defining our stack

The module structure

The URI space

The data space

The message broker

Project – Modular Monolith

Sending events from the catalog module

Consuming the events from the basket module

Inside the aggregator

Exploring the REST API HttpClient

Sending HTTP requests to the API

Validating the existence of a product

Transitioning to microservices

Challenges and pitfalls

Summary

Questions

Further reading

Answers

An end is simply a new beginning

Other Books You May Enjoy

Index

Landmarks

Cover

Index

Share your thoughts

Once you’ve read Architecting ASP.NET Core Applications, Third Edition, we’d love to hear your thoughts! Please click here to go straight to the Amazon review page for this book and share your feedback.

Your review is important to us and the tech community and will help us make sure we’re delivering excellent quality content.

Download a free PDF copy of this book

Thanks for purchasing this book!

Do you like to read on the go but are unable to carry your print books everywhere?

Is your eBook purchase not compatible with the device of your choice?

Don’t worry, now with every Packt book you get a DRM-free PDF version of that book at no cost.

Read anywhere, any place, on any device. Search, copy, and paste code from your favorite technical books directly into your application. 

The perks don’t stop there, you can get exclusive access to discounts, newsletters, and great free content in your inbox daily

Follow these simple steps to get the benefits:

Scan the QR code or visit the link below

https://packt.link/free-ebook/9781805123385

Submit your proof of purchaseThat’s it! We’ll send your free PDF and other benefits to your email directly

Section 1

Principles and Methodologies

This section delves into the core architectural principles and testing methodologies that form the backbone of modern software engineering practices, setting a solid foundation for the book. By exploring these essential concepts, you gain the insights needed to navigate the complexities of software architecture and ASP.NET Core development.

We begin with an introduction to the book’s structure and the essential knowledge required to make the most out of it, including a brief overview of design patterns, their significance, and the role of experience in shaping architectural decisions. We emphasize understanding rather than memorizing patterns, encouraging you to experiment and learn through hands-on experimentation.

The journey continues with a look at automated testing, a critical component of software quality assurance. Chapter 2, Automated Testing, introduces the fundamentals of testing, including unit tests, integration tests, and the principles of test-driven development (TDD). With a focus on the xUnit framework, you will learn how to apply various testing strategies to improve code reliability and maintainability. This chapter introduces integrating testing into the development process, emphasizing its role in modern workflows.

We explore architectural principles in Chapter 3, Architectural Principles, where we dissect the building blocks of software architecture, including discussions on responsibility segregation, the significance of design patterns, and strategies for creating robust and scalable systems. The chapter aims to equip you with the knowledge to make informed architectural decisions, emphasizing the rationale behind common design choices.

Chapter 4, REST APIs, covers REST APIs, an essential aspect of modern web development. This chapter provides a practical guide to designing and implementing RESTful APIs, including best practices and considerations for security, performance, and scalability. Through this exploration, you will understand how REST APIs fit into the architectural landscape.

By the end of these chapters, you will understand crucial concepts and have a strong foundation in testing methodologies, architectural principles, and REST APIs. Tying these concepts together lays the groundwork for a successful journey into software architecture, development, and the rest of the book.

This section comprises the following chapters:

Chapter 1, IntroductionChapter 2, Automated TestingChapter 3, Architectural PrinciplesChapter 4, REST APIs

1

Introduction

This book organizes chapters according to scale and topic, which allows you to start with a solid foundation and gradually build upon it, much like constructing a program. We aim to delve into the thought processes behind the systems we design from a software engineer’s perspective.

While many resources focus on presenting just a handful of ways to apply design patterns, this book diverges from that path. We emphasize understanding the underlying principles and the rationale behind architectural choices hands-on.

This is not a magic recipe book. From experience, there is no magical recipe when designing software, 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 who never fail are the ones who never do anything.

At a high level, this book introduces you sequentially to topics including:

Principles and methodologies: Automated testing, architectural principles, the fundamentals of REST APIs, and so onDesigning with ASP.NET Core: ASP.NET Core mechanisms, such as minimal APIs, MVC, and dependency injectionComponent patterns: Create small chunks of software by leveraging Gang of Four structural and behavioral patternsApplication patterns: Explore ways to structure an application, from layering to microservices architecture

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 the task at hand 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 will show you how to mix and match patterns to think “architecture” instead of giving you step-by-step instructions to reproduce.

The rest of this chapter introduces the concepts we explore throughout the book. We cover refreshers on a few notions—for intermediate and seasoned developers—to ensure everyone is on the same page. We also touch on .NET, its tooling, and some technical requirements.

In this chapter, we will cover the following topics:

What is a design pattern?Anti-patterns and code smellUnderstanding the web: request/responseGetting 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.

”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. 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 must do is very similar: put small pieces together to create something bigger. With a lack of guidance and experience, your construction might not be as sturdy as it could be. With that analogy in mind, a design pattern is a plan to assemble a solution that fits one or more scenarios. For example, when building a castle, we could design a single tower (a plan) and produce it multiple times by following the same steps (instances of the plan). 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.

Applying design patterns effectively will improve your designs, 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—and 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 have made sure 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 are proven bad architectural practices, while code smells are tips about possible flawed design. Learning about bad practices is as important as learning about the best ones. The book highlights multiple anti-patterns and code smells to help you get started. Here, we briefly explore a few of them.

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 labeled 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 begin with an example. 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 that 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, Strategy, Abstract Factory, and Singleton Design Patterns.

Code smell: long methods

The long methods code smell is when a method extends to more than a certain number of lines of code. For most teams, 10 to 15 lines is enough to fall into this specific case. 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 CQS, which you will learn about in Chapter 12, Behavioral Patterns, and Chapter 16, Mediator and CQS 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, as an ASP.NET Core developer, 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:

The communication starts.The client sends a request to the server.The server receives the request.The server does something with the request, like executing code/logic.The server responds to the client.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.

Here is a quick reference table; we will explore the concept of idempotence afterward:

Method

Request has body

Response has body

Idempotent

GET

No*

Yes

Yes

POST

Yes

Yes

No

PUT

Yes

No

Yes

PATCH

Yes

Yes

No

DELETE

May

May

Yes

Table 1.1: Structure of common HTTP methods

* 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 DELETE request multiple times should delete a single entity (idempotent), while sending the same POST request multiple times should create multiple similar entities (not idempotent). 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, REST APIs.

Now that we have explored HTTP methods, let’s have a look at an example of a GET request to become more familiar with HTTP:

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 and request size

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). An 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, which mainly contains the response header as I removed most of the body for brevity reasons:

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 <!DOCTYPE html> <html lang="en"> Response body truncated for brevity </html>

Now that the browser has received the server’s response, it renders the HTML web page. 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 ASP.NET Core session state (leveraging cookies under the hood), manually using cookies, or we could create a stateless application. I recommend going stateless whenever possible. We will write primarily stateless applications in the book.

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, built upon the Open Systems Interconnection model (OSI model), which defines seven layers. 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. Understanding the book’s content does not require OSI proficiency, but you cannot have too much foundational knowledge. See the Further reading section for more on this subject.

A packet is a chunk of data that is transmitted over the wire. We could send a large payload directly from a source to a destination machine, but that is not practical, so the network layer breaks down the payload into smaller packets. The network layer uses the maximum transmission unit (MTU) to know whether it needs to break down payloads.

For example, Machine A (source) sends a file to Machine B (destination), and the router (network layer) breaks the payload (file) into multiple packets, sends them to Machine B, and then Machine B reassembles the packets back into the file sent by Machine A. 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

Let’s start with 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 non-Windows OSs. 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 Software Development Kit (SDK) and runtime that 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 are released every year now. Even-numbered are Long-Term Support (LTS) releases with free support for three years, and odd-numbered 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:

ApplicationsLibraries

Applications target a version of .NET, such as net5.0 and net6.0. Examples of that would be an ASP.NET Core 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. A TFM is a way to identify and target a certain version of .NET (net8.0 targets .NET 8 while net8.0-ios targets .NET 8 for IOS devices). 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 OS-specific APIs like net8.0-android and net8.0-tvos, which give access to OS-specific APIs. You can also target multiple TFMs when needed.

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.

The command-line interface versus Visual Studio Code versus Visual Studio

How can one of these projects be created? .NET Core comes with the dotnetcommand-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