- Published on
Where did my methods go?
- Authors
- Name
- Damian Płaza
- @raimeyuu
Introduction
My studies weren't related to software engineering or computer science. There were oriented toward control engineering, embedded programming and topics linked to those.
Initially, everything was procedural, one-file/one-script programs. Signal or image processing? Pure and genious ideas embodied as a MATLAB script file. Working with microcontrollers? Harsh-reality C/C++ programs. Programming was just a vessel for materializing great thoughts from the Giants of the various scientific and engineering fields.
Not objectively about objects
It wasn't that astonishing or amazing moment, when I saw objects in action. It felt different when suddenly I was able to bring something to life and it had so-called "methods". They were able to manage a state. Actually, this looked very "natural". Of course - first attempts (and next ones too) were far from SOLID, GRASP - I would say closer to STUPID (we like acronyms, don't we?). It wasn't the best, high-quality code, but it was mindset wrecking in the way one is completely mesmerized by it.
The first serious "introductory" book was Head First C#. "Novel" approach for transferring knowledge, combined with practical projects, gave birth to curiosity about modelling. (I didn't think of it in that way back then)
This was big "aha" moment - "so that's what object-orientation is about".
Principal Component Analysis, Kalman Filter, Support Vector Machines, Bees Hive Simulator - each, from my perspective, shares similar principle - clear focus on the problem itself, not taking into account "the surrounding". Either a stateless algorithm implemented procedurally or a small simulator flavoured with object-orientation - pretty much the same. Cracking the problem itself.
Where are my methods?
I recall the first contact with such, I assume quite popular, way of programming true business applications (I haven't experience identical code, it's more about the metaphor). Here's the domain layer:
// Domain/User.cs
public enum UserStatus
{
Registered,
Blocked,
Rejected
}
public class User
{
public Guid Id { get; set; }
public string Email { get; set; }
public UserStatus Status { get; set; }
}
And then in business logic layer:
// Services/UserService.cs
public class UserService : IUserService
{
// some very important properties and DI via constructor
public async Task Register(string email)
{
var existingUser = await _userRepository.GetByEmail(email);
if(existingUser != null && existingUser.Status == UserStatus.Registered)
{
throw new InvalidOperationException($"The user with email {email} already registered");
}
var newUser = new User
{
Email = email,
Status = UserStatus.Registered,
Id = Guid.NewGuid()
}
//...persistence by using high-tech storage
}
}
Maybe it's a matter of retrospective, but feelings were kind of orbiting around the following:
"Wait, where are methods on User
class? Ah, business application, so method is on the Service level."
"This is a Web API? Mhm, yes, now I got it, Service Layer is responsible for handling the logic."
"Layers? I see. There is a Domain layer with entities and Service layer serving the requests."
"Domain and Business Logic separation? Makes sense, User
is responsible for keeping the data, UserService
is reponsible for handling the operations on the data."
Somehow that was the "golden" standard, but actually I felt bad, because the beautiful knowledge I acquired through various activities was...Useless? Maybe because I haven't had enough commercial experience with business applications! Also, methods were there, but moved one layer above.
"Functions? I suppose you meant methods, right?"
Nothing hits you as hard as JavaScript.
Classes? Nope, a prothesis trying to simulate so (bear in mind it was some time ago).
Inheritance? Well, kinda. "We have prototypes".
Encapsulation? Oh, you have module pattern in order to keep things invisible for the outside world.
"What is this little thingy that isn't attached to any object, but floats around here and there?"
A FUNCTION.
Nothing more, nothing less. Accepting some data, returning some data (or not). Getting, e.g. a collection of selected emails and requesting email delivery to each of the email recipients. How cool is that?
Of course, there were other things I didn't take into account, but that was the first "awareness" moment of other side of the coin. Next "aha" I had. I have already experienced "the great division" between the data and methods so somehow this felt very attractive (and JavaScript oriented).
Welcome to Fantasy Land and enjoy your stay!
I recall finding ramda. Now it sounds a bit silly, but - it felt like a bliss. Functions, functions everywhere.
There was some "strange" notation, but somehow it made sense after giving it a bit of thinking. Even though I wasn't using TypeScript back then, those signatures gave some understanding how to utilize API. It was all "hardcore" function-abuse programming from my side.
Am I proud of it? Well, maybe.
Did I learn something? A lot.
I stepped from the main path and followed the white rabbit, eventually founding ramda-fantasy. Naturally, opened another "pandora's" box (or should I say monad box?) labelled fantasy-land. I haven't had chance (or rather courage?) to introduce such concepts to the codebases. Functions were cool, simple to understand, sometimes not that easy (especially when you try to do point-free programming without knowing that the blade will swing back soon).
Despise
At some point of such wild exploration I found F#.
The sympathy for the language, the powers it gives, the beautiful terseness it brings, stays with me all the time. I recall devouring everything I could related to the syntax, concepts. Finally, I was able to experience what I read until that moment - immutability, structural equality, composition and more. Modeling capabilities I have never seen.
Even though it wasn't as pure as Haskell (yes, I found out too), still the major attracting point was .NET ecosystem. I somehow felt rooted into it, because I started my journey with C#.
Ah, C#.
Impure, barbarous and conceptually inadvanced OO language (please note I am joking and exaggerating here purposefully). The great despise started. When I saw that clear division between data and behavior is possible, everything became simple.
My functions were publicly available, all modules were open so I could mix and mash up whatever I imagined. But functional programming, right?
Doesn't matter. Encapsulation is for OO. Who needs that? I could create worlds by modeling them with ADTs. Then use pattern matching to be exhaustive with my intentions. Interfaces? Oh no, no. We are not doing OO here.
"How do I represent a cohesive set of behaviors? Of course, a record of functions. Isn't that too easy?!"
Blue book and necromancy
It wasn't that great.
After some point I saw that detached functions, floating like bees over the flowers full of nectar, working on records string
, int
, bool
felt similarly to User
and UserService
from past experience (or objects with string
, number
, boolean
, depending if I wandering in JavaScript world).
I mean, I wasn't suffering from having unmaintainable codebases (maaaaaybe when not having TypeScript). I had an impression that yet again I am missing something.
Pure functions were pure joy to write, but did I solve the right problems? What were I optimizing for? For functional programming paradigm utilization?
Composable types thinking in my pocket, combined with function pipelining, should be a silver-bullet to all problems, right?.
But, as someone pointed out very adequately, "a good developer is like a werewolf: afraid of silver bullets".
"Silver-bullet" thinking might bring havoc. It is also known as "hammer and nail" metaphor.
From "god mode" I went to exploration mode, again. And in almost like Indiana Jones journey I found a blue gem - "the bible" of Domain-Driven Design. Tactical Patterns - Aggregates, Value Objects, Repositories, Domain Services - more patterns to learn, hurray!
I got lured to the beautiful world of DDD tactical patterns. I don't even remember that I paid attention to bounded contexts, context maps and ubiquitous language.
And then I saw them.
AGAIN.
Methods! Objects had methods! Can you believe that?! Such object was no longer just an object. It was called "a model". Nevermind, I finally found "my" methods!
Yes, I know that functional programming is all we need, but the way of how "the model" was presented and expressed was looking so...Natural? Complete?
Googling, reading, watching - it turned out that people were able to capture core points of the "domain" withtin boundaries of OO classes, consisting of both methods and fields. Expressing vital parts of the concepts was possible. Of course, such "models" were not able to handle HTTP requests, put messages on queues. Those were not their responsibilities. However, they looked complete.
Summoning "a walking skeleton" to see if we understood business intentions correctly - somehow it clicked.
What's more, I found another gem, but this time it was white - "the functional bible" of Domain-Driven Design.
Maybe there were no objects with methods, but it didn't matter. Suddenly, "a walking skeleton" became expressable on a different plane - type plane. It related quite well to what I have learned in the blue book - without seeing "only objects with methods" or "only functions and data".
I haven't had a chance to introduce it to the codebase neither, but my mental models (pun intended) changed. I stopped "despising" and started "appreciating".
The Moon
After some time I understood that functions, composable types or objects with behavior - it's not the point. Almost like in the Zen Koan - "when the master points at the Moon with the finger, fools look at the finger, not at the Moon" - I was the one of those fools. I am not neglecting how used language shapes our way of thinking, but at some stage it is a finger and not the Moon.
Then, what is the Moon?