Category: Notes

Notes on courses, videos, and articles I’ve consumed. When possible I’ve linked back to the source material.

Domain Driven Design Notes

Why DDD?

YAGNI – You are not going to need it.

  • Shortening development time

KISS – Keep it short and simple

  • Maintainable

DDD

  • Focus on essentials parts
  • Simplifying the problems.

We can only handle so much complexity in our brains.

Ubiquitous language

  • Use the same language between the coders, QA, BA, Clients

Bounded Context

  • Separate area’s of the system that don’t make sense to be together.

Core Domain – Focus on the most important part of the system. Think of Core Competency in business

When to unit test Where Test Coverage meets value. At some point, adding tests doesn’t add value. I.e. principle of diminishing returns

Types of Equality Identifier equality – do the Id’s between the entities match? Reference equality – do the two objects point to the same reference in memory Structural equality – do the two objects have the same values.

Entities vs. Value Objects

  • Entities
    • Have inherent Identity
    • Reference equality
    • Identifier equality
    • Single place for equality members
  • Value Object
    • Value objects don’t have an identifier field.
    • Can be treated interchangeably
    • Immutable
    • Can’t live on their own, must belong to one or several entities

How to Recognize a Value Object

  • Value objects are light-weight
  • Put most of business logic to value objects
  • Entities act as wrappers

When to create a Repository Repositories are generally created for each aggregate root.

Null Object Design Pattern

Command Query Separation Principle

Anti-Corruption Layer

Bounded Context is a logic separation of the domain in the code. Some examples of this in use is putting all the code related to a Bounded Context under the same folder or using different schemas in the database. Yet, another possibility is different deployments.

O’Reilly’s Software Architecture Series

O’Reilly Learning:

Software Architecture Superstream Series

Trisha Gee

Anti-Patterns

Bad Code Reviews

A bad code review is a review that makes the person feel bad about the code they just spent hours working on.

We are human beings; instead of saying this is wrong, offer reasoning and suggest another direction.

Code reviews are about learning, collaboration, and conversation to improve the product, the company, and the people.

Book Recommendations

  • Delusions of Gender: How Our Minds, Society, and Neurosexism Create Difference
  • Programmed Inequality: How Britain Discarded Women Technologists and Lost Its Edge in Computing
  • Invisible Women: Data Bias in a World Designed for Men
  • 97 Things Every Java Programmer Should Know
  • Head First Design

Mark Richards

Software Architecture Styles

Architecture Styles vs. Architecture Patterns

Architecture Styles is a general strategy to an architecture. One or more architecture patterns can be used within an Architecture Style. Architecture Styles may also be hybrids. For example, an event-driven architecture may be made up of small components and with their data sources and deployed independently. This would be a microservice event-driven architecture.

Sonya Natanzon

The Role of the Software Architect

The Three Skills Architects must have

People

The architect must be an influencer. Value proposition, explaining the solution, and selling your ideas.

Conway’s Law. Org structure influences application structure. The lines of communication influence the application structure.

Growing new leadership. A lot of the time, this means training people.

Knowledge sharing.

Delegate to others and support others as they work through the problems. You can’t take on all the work yourself.

Create room for experimentation and have tolerance for mistakes.

Technical

Always learning new technologies and ideas. And then teaching and knowledge sharing. Information hoarding doesn’t work.

Selecting a toolbox. What technologies should our company use to be successful?

4 C’s

Analytical Skills

Thought

Evolve the Big Picture – Understand the path to how we got here and then chart a path forward. Create a roadmap for the next 6 months to a year.

Champion Process – People don’t fail you; the process does. This means having code reviews, retrospectives, following best prates. The process serves the team, not the other way around. Always be looking for ways to eliminate waste, whether this means adding process or removing it.

Set Culture and Values – Build, change, and influence culture and values.

The Four Immeasurables

Near Enemy and Far Enemy

  1. Loving Kindness
  2. near: selfish love
  3. far: ill will
  4. Compassion
  5. near: pity
  6. far: cruelty
  7. Empathic Joy
  8. near: blind euphoria
  9. far: jealousy
  10. Equanimity
  11. near: indifference
  12. far: paranoia
Near Enemies of Software Architect Attributes
  1. People
  2. near: functional management without influence means you become a taskmaster. You don’t have buy-in from your team.
  3. Tactical
  4. near: Individual Contributor rarely leaves time for a broader strategy. You don’t have the time to step back and see the big picture.
  5. Thought
  6. near: Ivory Tower Architect – They make architecture an academic exercise without real and practical value. They get hung up on the "right way" instead of being pragmatic and shipping software.

Solution Process

Problem Definition

  • problem statement
  • supporting analysis
  • as-is documentation

Solution Proposal

  • Models diagrams and other documentation
  • Technology selection
  • POC’s
  • Ubiquitous language definition

Implementation

  • API Definition, schemas, data models
  • production code
  • changes to everything are done in the previous phases.

Enhancement

  • Changes
  • Bugs
  • Maintenance

Book Recommendations

  • The Five Dysfunctions of a Team: A Leadership Fable – Patrick Lencioni
  • Multipliers: How the Best Leaders Make Everyone Smarter – Liz Wiseman and Greg McKeown

Nate Schutta

Thinking Architecturally

We can’t predict the future; we must decide on something concrete now and stick with it. We can’t be chasing the future. We also must plan for the future and move to technologies when it makes sense for the business. That might be because it gives us a competitive advantage or the cost of supporting our existing platform is rising.

In software, we don’t know. It’s about trial and error. Let’s try it and find out.

Every single choice has trade-offs. When someone comes to you and says, "This is the most amazing thing; it’s going to solve every problem we’ve ever had," you should immediately ask, "What are the trade-offs?" This is design, in a nutshell, this is architecture in a nutshell.

There are three answers to every computer science questions

  • 42, this is the geek check to see who is in the know and well-read
  • Another layer of indirection
  • it depends

Balancing when to use what tool over the other is the job of the architect. There is no perfect technology.

When presenting new technologies, you should be able to answer the following questions:

  1. What do you like about this technology?
  2. What DON’T you like about this technology? — you should have spent enough time with this technology to understand its shortcomings. If you haven’t, why are you recommending it?

"Quality Requirements" is another way to say the "ilities", for example, Scalability, Reliability, Simplicity.

There are two ways to sell you the idea:

  1. The hammer – I’m going to tell you this is a good idea
  2. The ninja – I’m going to convince you that it’s your idea and it’s a good one.

Sam Newman

Microservices

The Fallacies of Distributed computing apply.

Monolith does not mean a big bad legacy system. It’s just a bigger deployment package with multiple services/domains that share their data.

Fundamentally, Microservices are a type of distributed system.

Any distributed network had the concerns of latency.

You have the concerns of consistent data. Microservices embrace this idea of a single source of truth.

One of the things that set Microservices apart from other services is it does not share its data. It owns all its data. It’s truly an encapsulated service, including its data. There is no big shared database on the backend.

The problem of partitioning is when one service can’t see another service. This happens there is a disruption in the network.

The opinions of Microservices is that each service is independently deployable and that data that mutates is stored within a service boundary.

Microservices is end-to-end functionality modeling around the aspect of the business domain.

Microservices is the first post-DevOps system.

The Monolith is not the enemy. The Monolith is not the enemy; it’s not bad. A monolith is just a different style of architecture.

Microservices is typically not a place to start but a place to end. Start out discovering your business domain by building a modular monolith. When you’ve reached your current architecture limits, and Microservices is a good fit, move to a Microservice architecture.

Not all of your services need to be Microservices.

Microservice size is not about code but about the business domain, functionality, and data. It’s also about how easy is it to create a service? The easier a service is to create, the more and the smaller the services will be.

Start off with one or two services, then add more services as you are successful with the first services. Adding microservices is not an on/off switch but a dial was to increase the volume.

Stick with, you know. Don’t overload yourself with things to learn. If you know, Go, stick with it.

The only prerequisite to Microservices is Log Aggregation.

Data is accessed and mutated via the Microservice, not directly at the data source.

Microservices is a style of modular architecture. It’s data encapsulation at a service level.

Microservices is about functional slicing at the team and architecture level. This includes the UI level also.

Writing Allocation Free Code in C# By Matt Ellis

Twitter: @citizenmatt
GitHub: https://github.com/citizenmatt/RefSemantics

Always be measuring – don’t do it because you think it’s the right thing to do. It might not be.

Managed memory is cheap. That’s not true, allocation is cheap and garbage collection is expensive. Garbage collection stops the world while things are cleaned up and optimized. Sometimes memory cleanup can impact the performance of the application.

Performance Low Hanging Fruit

Object reuse

Object pooling. Pass in existing array rather than allocating a new one. Preallocate array length.

String concatenation.

Use StringBuilder, Preallocate length if possible.

params arguments

void MyParamsMethod(params string[] args)
{

//... 

}

In Code:
MyParamsMethod("Hello", "World");
MyParamsMethod();

Complier:
MyParamsMethod(new {"Hello", "World"});
MyParamsMethod(new [0]); // this creates a new array object in memory

Instead, do this in code:
MyParamsMethod(new {"Hello", "World"});
MyParamsMethod(Array.Empty); // reuses an existing empty string array in memory. It's the same idea behind string.Empty

Suggestion: Introduce overloads with common number of arguments.

Boxing

Boxing creates a new object on the heap. Now you have two memory locations with the same value. Changing either value does not impact the other value.

Suggestion: Introduce generic overloads

Closures

The complier converts closures to classes. Captured values are passed in as constructor parameters. This class is then allocated to the heap.

Suggestion: Avoid critical paths. Pass state as argument to lambda. Investigate local functions. Since local functions lifetime is known, it can be allocated on the stack where allocation and cleanup are cheap.

LINQ

Lambda expressions are treated the same as Closures. They are allocated as a class to the heap. Because much of LINQ is based on static methods, additional allocations of Iterators and IEnumerable happen.

Suggestion: Avoid critical paths. Use good old foreach and if statements

Iterators

iterators are rewritten as a state machine, which means more allocations to the heap.

Suggestion: Return a collection. Be aware of the cost.

async/await

Async/await also, generate a state machine. Task and Task also trigger more allocations which then can’t be reused.

Look into ValueTask for common uses cases.

Suggestion: Investigate ValueTask.

Heap Vs Stack

Each method pushes space onto the stack for local variables. When the method is exited the memory is popped from the stack.

Stack allocation and cleanup are cheap because the memory has a lifetime. However, stack space is limited.

*Why passing value types copies the data versus passing a references it’s important to note that copying data isn’t that expensive.

Reference Semantics with Value Types

Allows value types to be used like reference types
– pass by reference everywhere

User value types to reduce allocations, reduce memory traffic, etc
– Throughput!

Pass by reference to avoid copies, enables modifying, etc.

Very low-level micro-optimizations…
– But they’ll be used in the platform…
– (And games, and parsing, and serialization, and …)

*These are low level optimizations that should only be used in paths were performance matters.

C# 7.2 Reference Semantics with Value Types

Allocating a reference type has a cost, but passing it around is cheap.
Allocating a value type is cheap, but passing it around has a cost.

Why can’t it be cheap to allocate AND cheap to pass around?

in parameters

Pass value type by reference. Called methods cannot modify it.

Method argument modifier

Complements out and ref

Passed by reference

Method cannot modify original value

Compiler enforces safety with defensive copy when calling members

ref locals and ref returns (C# 7.0)

ref returns

Returns a reference to value the, not a copy of the value.

– return type of method becomes e.g. integer reference int& in IL

Lifetime of returned value must exceed the lifetime of the called method

  • e.g. a reference to a field or method argument. NOT a variable in the called method.
  • Not allowed on async methods.

Modifying this reference is the same as modifying the original value

  • e.g. return reference to array element, and update it in place.

Add ref modifier to method declaration return type, and to return statement

ref locals

Assign a ref return to a new variable will create a copy

  • The variable is a value type, not a reference. (Cannot assign int& to int)
  • A ref local is a variable that is a reference to a value type
  • Accessing the variable accesses the original value

Use a ref local to store the ref return result

Type inference with var will get the value type, not the ref modifier

  • Requires ref var to work as expected.

ref readonly returns

Returns a read only value type by reference

readonly struct

Immutable value types

in parameters and ref readonly can create defensive copies

  • The compiler doesn’t know if the struct’s methods will modify state

readonly struct – compiler enforces all fields and properties are readonly

Immutable

More efficient – no copies made when calling members

  • Improves performance (micro-optimization)

ref struct

Stack only value types

Declare a value type that can only be stack allocated

  • I.e can never be part of a reference type

This constrains lifetime to calling method

  • Also, cannot be boxed, cannot use inside a non-ref struct
  • Cannot use with async methods or iterators
  • Cannot be a generic parameters

Limited use cases

  • Working with stackalloc memory
  • Primarily for Span<T>

What does SPAN have to do with ref struct?

For thread safety, need to update all fields of Span atomically (tearing)

  • Whole point is performances – cannot use synchronization

Internal pointers require special GC tracking

  • Too many in flight at once is expensive

How can SPAN represent stackalloc memory is SPAN was on the heap?

Solution: Span<T> is a ref struct – can only be created on the stack

  • Constrained lifetime, single thread access

Span<T>

New type of unify working with any kind of contiguous memory

  • Arrays, array segments, strings and substrings, native memory, stackalloc, etc

Provides array-like API – indexer

  • ReadOnlySpan provides getter indexer only

Type safe – each elements is of type T

Array-like performance

  • Not quite, but newer runtimes have special support

Slicing

  • Create a new Span with a sub-section of existing – without allocations!

Span<T> Implementation

Value Type – struct

System.Memory NuGet package

  • .NET Standard 1.1 (.NET Framework 4.5)+

New APIs and overloads in the BCL

  • E.g. String.AsSpan(), Stream.ReadAsync(), Utf8Parser.TryParse()
  • Significant usage of ref semantics – allocation free!

Span, ReadOnlySpan, Memory

Two versions – “portable” and “fast”

  • fast requires runtime support

Span<T> Performance – Portable Implementation

Portable works on .NET Standard 1.1 and above

  • .Net Framework 4.5+

Portable is not slow

  • But not as fast as arrays

Three fields – object reference, internal offset and length

  • Slightly larger than fast version, dereferencing is slightly more complex operation.

Span<T> Performance – Fast Implementation

Fast requires runtime support

  • .Net Core 2.1

Only has two fields – “byref” internal pointer and length

  • Slightly smaller struct and accessing an element is slightly simpler operation

Specific JIT optimizations

  • e.g elding bounds check in loop, like arrays

Very close to array performance