Domain Modeling Made Functional: Takeaways

If you are curious about the functional world, but you found it too hard to start, this book is a good place to start. These are my notes and takeaways from “Domain Modeling Made Functional” by Scott Wlaschin.

“Domain modeling made functional” teaches to start coding only after understanding the domain. And to capture requirements, constraints, and business rules using types in the system.

This book covers from Domain-Driven Design (DDD) to type systems to refining an existing domain model. It’s a jump to the functional world by designing a system to price and ship orders in a supply store. No jargon or weird concepts needed.

All the code samples are in F#. Most of the time, they’re easy to understand. Since F# has a better type inference than C#, types aren’t explicit all the time while writing functions. Some code listings are hard to translate to C#.

To follow the code using C#, I had to rely upon libraries like OneOf and Optional to bring discriminated unions and option types, not built into the C# language yet.

1. DDD and Ubiquitous language

The goal of DDD is to share the business model between developers and domain experts. In DDD, the business domain drives the design, not the database schema. Don’t rush to think in terms of database tables.

Write code using the same vocabulary from the business domain. Domain experts or product owners don’t think in integers or strings. But, in business concepts like OrderId, ProductCode. When in doubt, ask your domain expert what an OrderBase, OrderHelper mean.

2. Types everywhere

A type is a name for all possible input and output values in a function. For example, Math functions from one set to another.

There are two ways to construct types: AND types and OR types. An AND type is a combination of other types. And an OR type is a choice between a known set of types. OR types are also called discriminated unions or choice types.

For example, a meal is a combination of an entrée, a main course, and a dessert. And, a dessert is either a cake or a flan. To represent a meal, we need an AND type. And for a dessert, an OR type.

These would be the Meal and Dessert types in F#,

type Meal = {
  Entree: EntreeInfo
  MainCourse: MainCourseInfo
  Dessert: DessertInfo
}

type DessertInfo =
  | Cake
  | Flan

In the Object-Oriented (OO) world, types are like classes without behavior or single-method interfaces. Types represent a set of functions too. AND types are regular classes with properties. While OR types are enums with, possibly, members of different types.

plumbing supplies in Home Depot
Who needs plumbing supplies? Photo by Oxana Melis on Unsplash

3. Restrictions, Errors, and Invalid State

Restrictions

Express restrictions in your design and enforce constraints with new types.

For example, to represent unit quantities in orders, don’t use a plain integer. “Unit” is a concept in the business domain. It should be in a separate type, like UnitQuantity.

To restrict unit quantities between 1 and 1000, create a private constructor in the UnitQuantity type and only expose a factory method with the validation.

To enforce that an order should have at least one line item, create a NonEmptyList instead of a possibly empty List.

To represent optional values, use Option<T> instead of null.

Errors and Exceptions: Signature method honesty

Make errors part of your domain. Follow the Signature Method honesty. This means documenting all possible outputs in the signature of methods.

For example, a Divide function shouldn’t throw an exception. Instead write, int Divide(int a, NonZeroInt b);

Stay away from exceptions and instead use a Result type to wrap failed and successful types.

For example, to check addresses return Result<CheckedAddress, AddressValidationError>. It means either a valid address or a validation error.

type CheckAddressExists = UnvalidatedAddress -> Result<CheckedAddress, AddressValidationError>

type AddressValidationError = 
  | InvalidFormat of string
  | AddressNotFound of string

To work with exceptions in third-party code, wrap that code in a function that catches exceptions and returns a Result. Don’t catch all exceptions, only those relevant to the domain. Like, timeouts or failed logins.

Make illegal state unrepresentable

Convert a design with two choices to a design with one type per choice. Booleans are generally a bad design choice.

To represent validated emails, don’t create a CustomerEmail with an IsVerified flag. Instead, create separate types, VerifiedEmailAddress and UnverifiedEmailAddress.

With types, we don’t need unit tests for invalid situations, we have compile-time unit tests. Also, types better document the business domain.

4. Transformation-oriented programming

Write actions in your system as workflows or chains of transformations.

A workflow is a pipeline where the output type of every step is the input of the next one. A workflow receives commands and returns events as outputs. A command should have everything needed to start a workflow.

Put all the I/O at the edges. Call the database at the beginning or at the end of a workflow.

For example, these are the types of a PlaceOrderWorkflow and the types of its steps.

type ValidateOrder =  UnvalidatedOrder -> ValidatedOrder
type PriceOrder = ValidatedOrder -> PricedOrder
type AcknowledgeOrder = PricedOrder -> AcknowlegementSent option
type CreateEvents = PricedOrder -> PlaceOrderEvent list

type PlaceOrderWorflow = PlaceOrderCommand -> Result<PlaceOrderEvent list, PlaceOrderError>

5. Challenges

After reading this book, there are two things I want to change in the codebases around me.

1. Replace Constants class with enums

First, don’t use a Constants class for an exhaustive list of known values or options. Use enums instead.

public static class Constants
{
    public static readonly string Authorize = nameof(Authorize).ToLower();
    public static readonly string Capture = nameof(Capture).ToLower();
    public static readonly string Refund = nameof(Refund).ToLower();
}

public PaymentResponse PerformOperation(string operation, PaymentRequest request)
{
    // ...
}

By using strings as parameters, we can pass any string to the receiving method. But, the only valid options are then ones inside the Constants class. Enums are better to make illegal states unrepresentable in this case.

public enum OperationType
{
    Authorize,
    Capture,
    Refund
}

public PaymentResponse PerformOperation(OperationType operation, PaymentRequest request)
{
    // ...
}

2. Separate states, separate classes

Last challenge, don’t use a single class with a boolean property to represent two states. Use two types instead.

For example, don’t write a PaymentResponse with a HasError flag for failed and successful payments. Like this,

public class PaymentResponse
{
    public bool HasError { get; set; }

    public bool ErrorMessage { get; set; }

    // ...
}

Instead, write two separate classes: FailedPayment and SuccessfulPayment. Like this,

public class FailedPayment
{
    public bool HasError { get; } = true;

    public bool ErrorMessage { get; set; }

    // ...
}

public class SuccessfulPayment
{
    // No HasError and ErrorMessage...
    // ...
}

Also, as part of this last challenge, not only separate a class with a boolean type into two classes, but also separate optional or nullable state to avoid NullReferenceException.

6. Putting types into practice

I had the chance to practice what I’ve learned with this book, even before finishing it.

Here are my requirements. After getting a reservation, hotels want to charge a deposit before the guests arrive. They want to charge some nights, a fixed amount, or a percentage, either after getting the reservation or before the arrival date.

This is what I came up with. Better than a single class full of string, bool, and int properties. That would have been my approach before reading this book.

public class Deposit 
{
    public Charge Charge { get; set; }
    public Delay Delay { get; set; }
}

public abstract class Charge {}
public class Night : Charge
{
    public int Nights { get; set; }
}
public class FixedAmount : Charge
{
    public decimal Amount { get; set; }
    public CurrencyCode Currency { get; set; }
}
public enum CurrencyCode
{
    USD,
    Euro,
    MXN,
    COP
}
public class Percentage : Charge
{
    public decimal Amount { get; set; }
}

public class Delay
{
    public int Days { get; set; }
    public DelayType DelayType { get; set; }
}
public enum DelayType
{
    BeforeCheckin,
    AfterReservation
}

7. Parting thoughts

Voilà! These are the lessons I learned from this book. It’s a good introduction to DDD and functional programming. If you’re not a functional programmer, you can still take advantage of the concepts of this book in your everyday programming. I did.

You can skip the last 2 or 3 chapters. If you’re new to F# and want to work with it, they contain good examples of serialization and databases. But, if you want to adopt these concepts to your OO world, you can skim these chapters for the main concepts.

“A class with a boolean to represent two states is generally a smell.”

I liked that one.

For more takeaways, check Clean Coder and The Art of Unit Testing. Don’t miss, A case of Primitive Obsession, it shows how to put in place classes to replace plain primitive types.

Happy coding!

It's not what you read, it's what you ignore

A long time ago, I watched an online presentation by Scott Hanselman (@shanselman) about productivity. One of the best I’ve seen. These days I found my handwritten notes about it. Today, I want to share my takeaways and the tools I’ve used since I watched it the first time.

Triage every inbox

Do triage the same way doctors do it. Set three goals for your day, week, and year.

Separate your tasks into four categories divided into two quadrants: important versus urgent.

  • If something is Urgent and Important, act on it immediately. Think of a server down.
  • If something is Important but Not Urgent, it’s your regular job. Work on it.
  • If something is Not Urgent and Not Important, delegate it. Ask somebody else to do it.
  • If something is Not Urgent and Not Important, drop it. Ignore it.

Don’t read email first thing in the morning

Create rules in your email. Separate your emails into folders. For example,

  • Inbox for regular emails
  • CC for emails where you’re CCed. These are FYI emails
  • Bosses for your managers, CTO and CEO
  • External for emails from customers and clients

Conserve your keystrokes. Imagine your number of keystrokes is limited. Write 3 or 5 sentence emails. Anything longer should be in a wiki, blog post, or documentation.

timer on a table
Photo by Marcelo Leal on Unsplash

Use Pomodoro technique

Work in 25-minute sessions of intense concentration between 5-minute breaks. After 4 or 5 work sessions, take a longer break for 15 minutes. This is the Pomodoro technique in a nutshell.

During your Pomodoros, keep track of your internal and external interruptions. For example, I keep my cell phone out of sight and in silence mode while working on something important.

Don’t set up a guilty system

Put on your desktop what you really are going to do. Don’t pile up books on your desktop.

When you feel overwhelmed, sync to paper. Write down what you have to do.

“If it’s not helping me to <put-your-own-goal-here>, if it’s not improving my life in some way, it’s mental clutter and it’s out”

Voilà! These are some of my takeaways. I learned from this presentation to keep my emails separated into folders and to consider my keystrokes limited. For example, I’ve written some of my posts to answer friends and coworkers. That way I can help more than one person while keeping my keystrokes limited.

For more content on productivity, check my Visual Studio setup for C# and some tools that saved me 100 hours. For other presentations, check Livable Code.

Happy coding!

Want to ace your next take-home coding exercises? Follow these 13 short tips

Take-home coding exercises aren’t rare in interviews.

I’ve found from 4-hour exercises to 2-day exercises to a Pull Request in an open-source project.

Even though, in my CV I have links to my GitHub profile (with some green squares, not just cricket sounds) and my personal blog (the one you’re reading), I’ve had to finish take-home exercises.

Yes, hiring is broken!

For my last job, I had to solve a take-home challenge when applying as a C#/.NET backend software engineer. It was a decent company and I wanted to play the hiring game. So I accepted.

After I joined I was assigned to review applicants’ solutions.

Here are 13 short tips to help you solve your next take-home interview exercise. These are the same guidelines I used to review other people’s take-home exercises.

Stick to standards

Follow good practices and stick to coding standards. Write clean code.

1. Write descriptive names. Don’t use Information or Info as name suffixes, like AccountInfo. Keep names consistent all over your code. If you used AddAccount in one place, don’t use AccountAdd anywhere else.

2. Use one single “spoken” language. Write variables, functions, classes, and all other names in the same language. Don’t mix English and your native language if your native language isn’t English. Always prefer the language you will be using at the new place.

3. Don’t keep commented-out code. And don’t have extra blank lines. Use linters and extensions to keep your code tidy.

4. Use third-party libraries to solve common problems. But don’t keep unused libraries or NuGet packages around.

5. Have clearly separated responsibilities. Use separate classes, maybe Services and Repositories or Commands and Queries. But stay away from Helper and Utility classes full of static methods. Often, they mean you have wrong abstractions.

6. Add unit or integration tests, at least for the main parts of your solution.

If you’re new to unit testing or want to learn more, don’t miss my Unit Testing 101 series where I cover everything from the basics of unit testing to mocking and best practices.

Framed wall art
Write code you would print and hang on a wall. Photo by Lefty Kasdaglis on Unsplash

Version Control your solution

Keep your solution under version control. Use Git.

7. Write small and incremental commits. Don’t just use a single commit with “Finished homework” as the message.

8. Write good commit messages. Don’t use “Uploading changes,” “more changes,” or anything along those lines.

9. Use GitHub, GitLab, or any hosting service, unless you have different instructions for your take-home exercise.

Write a good README file

Write a good README file for your solution. Don’t miss this one! Seriously!

10. Add installation instructions. Make it easy for reviewers to install and run your solution. Consider using Docker or deploying your solution to a free hosting provider.

11. Add instructions to run and test your solution. Maybe, some step-by-step instructions with screenshots or a Postman collection with the endpoints to hit. You get the idea!

12. Tell what third-party tools you used. Include what libraries, NuGet packages, or third-party APIs you used.

13. Document any major design choices. Did you choose any architectural patterns? Any storage layer? Tell why.

Voilà! These are my best tips to succeed at your next take-home interview challenge. Remember, it’s your time to shine. Write code as clean as possible and maintain consistency. Good luck!

If you want to see how I followed these tips on a real take-home coding exercise, check SignalChat on my GitHub account. It’s a simple browser-based chat application using ASP.NET Core and SignalR.

canro91/SignalChat - GitHub

Get ready for your next interview with these tips for remote interviews. And, to prepare for your technical interviews, check how to evaluate a postfix expression, how to solve the two-sum problem, and how to shift array elements. These are real questions I got in previous interviews.

Happy coding!

BugOfTheDay: There are pending requests working on this transaction

These days I got this exception message: “The transaction operation cannot be performed because there are pending requests working on this transaction.” This is how I fixed it after almost a whole day of Googling and debugging.

To fix the “pending requests” exception, make sure to properly await all asynchronous methods wrapped inside any database transaction.

Pipeline pattern and reverting reservations

I was working with the pipeline pattern to book online reservations.

The reservation process used two types of steps inside a pipeline: foreground and background steps.

The foreground steps ran to separate enough rooms for the reservation. And the background steps did everything else to fulfill the reservation but in background jobs.

If anything wrong happened while executing the foreground steps, the whole operation rollbacked. And there were no rooms set aside for the incoming reservation. To achieve this, every foreground step had a method to revert its own operation.

The code to revert the whole pipeline was wrapped inside a transaction. It looked something like this,

using (var transactionScope = _transactionManager.Create(IsolationLevel.Serializable))
{
    try
    {
        await pipeline.RevertAsync();

        transactionScope.Commit();
    }
    catch (Exception e)
    {
        transactionScope.Rollback();

        _logger.LogError(e, "Something horribly horribly wrong happened");

        throw;
    }
}

The Commit() method broke with the exception mentioned earlier. Arrrggg!

Broken display glass
No displays or electronic devices were damaged while debugging this issue. Photo by Julia Joppien on Unsplash

Always await async methods

After Googling for a while, I found a couple of StackOverflow answers that mention to await all asynchronous methods. But, I thought it was a silly mistake and I started to look for something else more complicated.

After checking for a while, trying to isolate the problem, following one of my debugging tips, something like this code got all my attention.

public async Task RevertAsync(ReservationContext context)
{
    var reservation = context.Reservation;
    if (reservation == null)
    {
        return;
    }

    var updatedByUserId = GetSystemUserAsync(context).Id;
    await _roomService.UnassignRooms(reservation, updatedByUserId);
}

private async Task<User> GetSystemUserAsync()
{
    var systemUser = await _userRepository.GetSystemUserAsync();
    return systemUser;
}

Did you notice any asynchronous methods not being awaited? No? I didn’t for a while. Neither did my reviewers.

But, there it was. Unnoticed for the code analyzer too. And, for all the passing tests.

Oh, dear! var updatedByUserId = GetSystemUserAsync(context).Id. This line was the root of the issue. It was meant to log the user Id who performed an operation, not the Id of the Task not being awaited.

Voilà! In case you have to face this exception, take a deep breath and carefully look for any async methods not being awaited inside your transactions.

If you want to read more content, check my debugging tips. To learn to write unit tests, start reading Unit Testing 101. A better failing test would’ve caught this issue.

To read about C# async/await keywords, check my C# Definitive Guide. It contains good resources to be a fluent developer in C#.

Happy bug fixing!

Monday Links: Workplaces, studying and communication

Another Monday Links. Five articles I found interesting in last month.

Don’t waste time on heroic death marches

“Successful companies, whether they’re programming houses, retailers, law firms, whatever, make their employees’ needs a priority.” Totally agree. No more comments! Read full article

How to study effectively

One of my favorite subjects: how to study. Don’t cram. Don’t reread the material. Don’t highlight. Instead, study in short sessions and recall the material. Easy! There are even more strategies. Read full article

Undervalued Software Engineering Skills: Writing Well

We, as developers, spend a lot of time writing prose, not only code. Commit messages, ticket and PR descriptions, README files. We should get better at it. To check my writings, I use the Hemingway app often. Read full article

women sitting on chairs
Once upon a time, there were no Zoom calls. Photo by Boston Public Library on Unsplash

15 signs you joined the wrong company as a developer

Number 12. and 13. are BIG red flags. Let’s pay attention to those. Recently, I read about “disagree with your feet.” It resonates with this article. When you don’t like something about your job and you can’t do anything about it, walk away. Read full article

No, we won’t have a video call for that

I don’t like those chat messages with only “Hi!” or “How are you?” when we both know that’s not the message. This article shows how to better communicate on remote teams. Embrace asynchronous communication. Prefer (in order) Issue tracker, Wiki, email, and chat. Stay away from video calls as much as possible. Don’t ping people on chat software. And other ideas. Read full article, Watch full presentation.

Voilà! This Monday Links ended up being about better workplaces. See you in a month or two in the next Monday Links! In the meantime, grab your own copy of my free eBook Unit Testing 101. Don’t miss the previous Monday Links on Farmers, Incidents and Holmes.

Happy reading!