Some days ago I read this tweet from @sendoaportuondo saying that creating a class for the mail field, the phone field, etc was crazy. I quickly asked who said that and he pointed me to Domain Driven Development.

After reading a little about that methodology I just agreed. It was a little crazy, I’m sure that there are reasons behind it but for me it seems just another crazy discipline in our field. It’s not the first time that I argued against all this dogmas that we follow without questioning, just because it’s well designed, even if at the end in the majority of cases it just results on having too many layers of complexity.

For this reason this post may seem a little hypocrite but here I go.

After working again with functional programing, after many years, and the powerful type system that the Swift compiler provides us I’ve connected the dots.

If you create types for everything you are using the compiler and it’s type system to ensure that your program is correct just by compiling it. It’s like having unit test automatically written by the compiler!

Let me give you an easy example. Imagine that you have this class Person and the sendMailTo function:

struct OPerson {
    let name: String
    let surname: String
    let mail: String
    
    init?(name: String, surname: String, mail: String) {
        self.name = name
        self.surname = surname
        
        // Don't validate mails with this stupid checks.
        if (mail.rangeOfString("@") == nil) || (mail.rangeOfString(".") == nil) {
            return nil
        }
        self.mail = mail
    }
}

I’m using here a struct, so we are seeing already some Swift coolness, but bare with me, this is just a class. In the initializer we validate that the mail is valid and if not we fail the initialization.

In some other place in our code we have a function that sends an email:

func sendMailTo(mail: String, # text: String) {
    println("Sending a mail to \(mail): \(text)")
}

We can use this like this:

let alex = Person(name: "Alex", surname: "Martinez", mail: "alexito4@gmail.com")
if let me = alex {
    sendMailTo(me.mail, text: "A super cool mail!")
}

If the object is created successfully, we can send the mail because we know that it’s valid. But then in another place of your program you have a mail that came from who knows where:

let mail = // this came from the hell...
sendMailTo(mail, text: "Nobody will receive this mail”)

Now, you cannot be sure that this will work because that mail nobody knows if it’s valid or not. Of course, you can validate it in the sendMailTo function, or create a Validator that you can reuse (because you want to reuse everything right?). Anyway you will have to write a bunch of Unit Tests to make sure that your App doesn’t crash on the the face of your boss.

But let’s try to do what DDD says. Take a look at this other approach for creating the same class Person:

struct Mail {
    let mail: String
    
    init?(mail: String) {
        if (mail.rangeOfString("@") == nil) || (mail.rangeOfString(".") == nil) {
            return nil
        }
        self.mail = mail
    }
}
struct Person {
    let name: String
    let surname: String
    let mail: Mail // <- THE KEY
    
    init?(name: String, surname: String, mail: String) {
        self.name = name
        self.surname = surname
        if let mail = Mail(mail: mail) {
            self.mail = mail
        } else {
            return nil
        }
    }
}

As you can see I’ve created a whole class just for the mail. I’ve also moved the validation in there, but believe me, that’s not the point that I want to make.

Now we can define the sendMailTo function like this:

func sendMailTo(mail: Mail, # text: String) {
    println("D Sending a mail to \(mail.mail): \(text)")
}

The usage is not different than before:

let alex = Person(name: "Alex", surname: "Martinez", mail: "alexito4@gmail.com")
if let me = alex {
    sendMailTo(me.mail, text: "A super cool mail!")
}

But now look at what happens with the mail that came from the hell:

let mail = "this is not an email"
sendMailTo(mail, text: "hi!") // 'String' is not convertible to 'Mail'

Here we go! String is not convertible to Mail. Compile error. Even in Swift this is a little verbose, but if you look at pure functional languages basically your code is just a bunch of types interacting, so it just a simple as that.