.NET Best Practices: Architecture & Design Patterns

5 days
UARCH
5 days

Upcoming Sessions

Date:

Format:

Price:

Location:

Book now

Date:

Format:

Price:

Location:

Book now

Date:

Format:

Price:

Location:

Book now

Date:

Format:

Price:

Book now

Interested in a private company training? Request it here.

Not ready to book yet? Request an offer here.

Improve your OO-design with S.O.L.I.D principles

The S.O.L.I.D principles were introduced by Robert C. Martin (also known as Uncle Bob) in 2000. The intention behind these principles is to make software designs more understandable, easier to maintain, and easier to extend. These principles are essential for every developer to know because they assist in writing better code and in better understanding code that was written with these principles in mind.

  • S - Single Responsibility
  • O - Open/Closed
  • L - Liskov Substitution
  • I - Interface Segregation
  • D - Dependency Inversion

Introduction to Patterns

One might not believe it, but design patterns did not originate in software architecture. Instead, patterns were first described in the 1970s by a real 'bricks and mortar' architect. A pattern is defined as a reusable solution to a common problem, without dictating the exact implementation. This characteristic makes patterns an ideal learning tool and a powerful means for communicating design ideas. Of course, there are also anti-patterns—approaches that are frequently used but are better avoided.

  • What is a Pattern?
  • The Gang of Four: Erich Gamma, Ralph Johnson, Richard Helm and John Vlissides.
  • Different kinds of design patterns: creational, structural and behavioral patterns.
  • Patterns everywhere: the difference between implementation, design and architecture patterns...
  • When to apply patterns, and when not to
  • Some anti-patterns, such as Loosy-Goosy

Singleton Pattern

We will start with a simple creational pattern, Singleton, and discuss its implementation in C#

  • What is a Singleton?
  • Singleton implementation choices in .NET
  • Why sealing classes is a good idea
  • LAB: Building a Singleton in .NET

Code Reuse with Template Method

Many times you need to provide a base class for developers to inherit from. Here we discuss building a more robust version using the template method pattern.

  • Template Method - defer exact parts of an algorithm to inheriting classes, delegates, ...
  • LAB: Building a Template Method for a board game

Abstract Object Creation with Factories

When developing software, you often find yourself continuously creating new objects. While the fundamental act of creating new objects is not inherently problematic, it can lead to design challenges or increased complexity. Creational patterns such as Factory Method can assist you in managing object creation in a way that is appropriate for the situation.

  • Factory Method - delegating/hiding the creation of objects to a factory
  • Abstract Factory - abstracting to a factory to create families of objects
  • LAB: Implementing an Abstract Factory

The Strategy and Chain-Of-Responsibility Pattern

Strategy and Chain-Of-Responsibility are two behavioral patterns that assist in defining a family of algorithms, encapsulating each one, and making them interchangeable. By applying these patterns, you can allow the client to choose the algorithm that best suits their needs at runtime.

  • Strategy - template method without the annoying inheritance
  • LAB: Sorting Objects using Strategies
  • Chain of Responsibility - strategy to go through a chain of strategies
  • LAB: Using a Chain of Responsibility to implement a Builder

Collection Patterns

Iterators are a fundamental part of the .NET framework. They provide a generic way of navigating through collections. By using the yield keyword, you can create an iterator that returns a sequence of values. This approach is particularly useful when you need to asynchronously iterate over a collection.

  • Iterator - providing a generic way of navigating through collections, yield is your friend, asynchronously iterating a collection
  • Performance considerations on implementing IEnumerable
  • Using Frozen collections

Improve Performance with the State and Flyweight Pattern

State and Flyweight are two structural patterns that help in managing object creation and state transitions. By applying these patterns, you can reduce memory consumption and improve the performance of your application.

  • State - defer state dependending logic to state classes, state machines,
  • Using the Stateless library
  • LAB: Implementing a VCR with the State patterns using the Stateless library
  • Flyweight - reduce memory consumption by preventing unnecessary creation of objects

Object Hierarchy Patterns

Composite, Interpreter, Builder and Visitor are four patterns that help in managing complex object structures and operations. By applying these patterns, you can simplify the design of your application and improve its maintainability.

  • Composite - Building Part-Whole Hierarchies
  • Interpreter: Build your own expressive language-grammar and execute it.
  • How LINQ uses Interpreter - and how you can take advantage of it yourself.
  • Builder: Hide how complex hierarchies of objects get built - and allow variations.
  • XAML as the ultimate builder
  • Reflection: the .NET way for implementing your own builder
  • How NOT to use reflection
  • LAB: Building your own calculator with Interpreter and Builder
  • Visitor: When you need a lot of different operations on the same object structure
  • Building a pretty-printer using Visitor
  • Implementing Visitor the dynamic way
  • LAB: Implementing a Visitor to walk over a complex hierarchy of objects

The Observer and Mediator Pattern

How do you design classes that are ignorant of each other, and still can communicate effectively?.

  • Observer - notifying whoever is interested in what you have to say, events vs. delegates
  • Mediator - providing two-way communication between objects unaware of one another, correcly implementing INotifyPropertyChanged
  • LAB: Avoiding the String-based programming anti-pattern

Replace Inheritance with Adapter, Decorator and Proxy

Created objects do not tend to stand on their own. No, they start encapsulating other objects trying to create structure in, what otherwise would be a chaotic software environment. Structural patterns help at identifying and setting up relationships between objects.

  • Adapter - plugging in different objects into your code that do not fit
  • LAB: Building INotifyPropertyChanged as a generic Adapter
  • Decorator - altering the behavior of an object without the caller realising it
  • LAB: Changing IComparable<T> with a Decorator
  • Proxy - proxying requests made to the subject without changing the behavior

Hide Implementation Details with Facade

Facade is both a GOF pattern and a Microsoft pattern. Here we will discuss the diffences and similarities between the two.

  • Facade - hiding the complexity of subsystems from the caller

Using Value Objects

The Value Object pattern represents objects that are defined by their values rather than a unique identity, such as a Money or EmailAddress type. Value objects are immutable and compared by their properties, making your code more robust, intention-revealing, and less error-prone.

  • Avoiding the Primitive Obsession anti-pattern
  • Best practices for building Value Objects
  • LAB: Building an E-mail Value Object

Accessing Data Using the Specification and Repository Pattern

The repository pattern abstracts data access logic by providing a collection-like interface for accessing domain objects, isolating the domain layer from data access concerns. It centralizes CRUD operations, making the codebase more maintainable and testable. The specification pattern allows you to encapsulate business rules or query criteria into reusable, composable objects, making your code more expressive and testable. It helps separate the "what to query" from "how to query," often used with LINQ to filter entities.

  • Repository - abstracting data access
  • LAB: Implementing a Repository using Entity Framework Core
  • Specification - encapsulating complex queries
  • Using the Specification pattern with Entity Framework Core
  • LAB: Implementing a Specification for a Repository

Using the Result Pattern

The result pattern refers to a coding convention where methods return a special type (often named Result, OperationResult, etc.) that encapsulates both success/failure status and associated data or error messages. This pattern improves error handling by avoiding exceptions for flow control and encouraging explicit success/failure checks in the calling code

  • Exceptions are expensive
  • Using the result pattern
  • Improving the result pattern using LINQ syntax
  • LAB: Using the result pattern in an MVC application

Build Flexible UIs with Model-View-Whatever

Most developers are not naturally skilled as graphic designers. This recognition has led to the development of design patterns that enable developers to concentrate on coding the application's behavior while allowing graphic designers to create compelling user interfaces. The cornerstone among these patterns is known as Model-View-Whatever (MVW), with Whatever being adaptable based on the specific technology in use. Grasping the MVW pattern is crucial, as it forms the foundation for developing both Windows and web applications.

  • Model-View-Controller: An ancient pattern back in fashion
  • The ASP.NET MVC Pattern
  • MVVM in Blazor - MVW taking advantage of powerful databinding capabilities
  • Command: Encapsulate behavior in objects
  • Implementing commands using closures
  • LAB: Using MVVM in a Blazor application

Design Patterns Applied: Developing your own Reusable Library

When do you most need patterns? The answer is particularly when you're developing a framework on your own. Building a framework involves integrating new features while maintaining backward compatibility, a challenge that can be significantly simplified through the proper application of patterns. Therefore, in this final part of the training, we will construct a reusable library. During this process, we will encounter several challenges and address them by applying the appropriate patterns.

  • Adding the GoF Command pattern to MVVM
  • Using interfaces for flexibility
  • Building Command Objects - extending ICommand's interface
  • Adding Undo and Redo functionality to the command pattern
  • Using a CommandManager class
  • Challenge: retro-fitting our commands into MVVM without lots of changes
  • Implementing Undo-Redo using the Memento pattern
  • Choosing whether or not to add the Prototype pattern
  • Ideas on how to proceed with the command pattern
  • LAB: Implementing Undo/Redo logic as a reusable library

So, how can you become a better developer? One of the best ways is to learn about design patterns. Design Patterns provide reusable solutions for common software design challenges. In this training, we identify software design problems and explore how to address them using the most suitable Design Pattern. We will discuss creational, behavioral and structural patterns. And of course we will explore implementation choices in .NET. All examples and labs use the latest LTS version of .NET and Visual Studio. Labs also work with any recent .NET and VS version.

This course is intended for experienced programmers who are familiar with C# and have a working experience of .NET.

Contact Us
  • Address:
    U2U nv/sa
    Z.1. Researchpark 110
    1731 Zellik (Brussels)
    BELGIUM
  • Phone: +32 2 466 00 16
  • Email: info@u2u.be
  • Monday - Friday: 9:00 - 17:00
    Saturday - Sunday: Closed
Say Hi
© 2025 U2U All rights reserved.