Oleg Sergienko

@i_am_os

CORE design pattern — the way out from overly complicated code

Have you ever found yourself stuck in the crazy amount of overly complicated code? No? But you know what it is, right? So let’s suppose you are a developer (at any level of evolution) or you are anyhow involved in product development, then this article is for you! Why? Let’s see the short answer, and then you’re welcome to read more about the solution to the problem you have likely come across.

Short answer

Working on various projects made me ask myself numerous times — why the model of the system in my head and its implementation in the form of code are way not the same things? And this “mismatch” actually results in overly complicated code. The short answer is that business logic and code have different life cycles, which implies more code to maintain for developers and longer development process and more expensive maintenance which is important to know for project managers.

More details, pls

Implementation takes place in the inconvenient “space” for our business logic — one of classes and variables, which leads to the problem of so-called “leaky abstraction”. The latter results in the difference between life cycles of business logic and code. While life cycle of business logic is quite stable ( it can be reused many times and during a long period). the life cycle of code part is short — it usually modified numerous times for the same business logic.

In case you’re not familiar with the term — leaky abstraction is an attempt to simplify interaction with (abstract from) an object (let’s say, a reusable part of system’s code) which leads to creating an abstraction model that is more complicated than the object itself. Sounds like something went wrong, doesn’t it? Indeed!

So, while trying to simplify things we are making them even more complicated actually. Yep. And finally, these differences between business logic and its technical implementation entail lots of unnecessary code. Is there any way to avoid it? I believe I’ve found one. (Well, it’s not just a belief if you know what I mean.)

The main idea is to move from another side — the one of business logic. And as it turns out, if we start describing the program logic using the language that is closest to business logic, we will be able to minimize the amount of unnecessary code and the need in different tricks. Also, it will be easier to debug and maintain the program, and thus the lifecycle of the code will be longer.

This is where CORE comes in

Imagine, you have a scene full of different characters that interact to each other in thousands of different ways. They eat, sleep, communicate to each other. They may fight against each other or don’t care at all about others. Sounds pretty much like real life, right? And so should it be! Keep this image in mind for a while — we will get back there shortly.

As we found out, overly complicated code happens when life (business logic) and its code representation have contradictions. This is where we need another programming concept that enables moving from real life to code, not backward. For this, I want to introduce you to CORE (an acronym of Context, Object, Request, and Event), which helps to build an architecture relying on business logic.

However, overly complicated code isn’t the only thing that you can fix with CORE. Sometimes your logic gets messed up, you have troubles with maintaining your code, or you are building an architecture from scratch — in all these cases (and many more, in fact), CORE will be helpful.

Acknowledging that you are in a such loop makes you create real solutions. (Have you been there? :D)

Alright, now let’s get back to our playground because — believe me, or not — CORE works in a very similar way. And you will see it yourself by taking this quick look at its main components:

  • Contexts

Do you remember the scene we talked about before? So here it is! Contexts are sort of a scene where our characters live and interact with each other. Let’s say we have a field with hedgehogs, which are our Objects.

  • Objects

Think about Objects in terms of characters each of which has their own needs and intentions, which they realize by means of direct activities or turning for help to other characters. So hedgehogs are our Objects, and they do some routine stuff — like eating, sleeping, playing with each other — for which they need Events and Requests.

  • Events

One of our hedgehogs (Objects) ate an apple, which is an Event. Loosely speaking, Events are activities that an Object owns, and they can’t be initialized by other Objects. Note that Events can’t be canceled: just like it’s in life — if something has happened to a character, it’s recorded in his personal history.

Here is how it may look in pseudocode:

Hedgehog = {
Event: {
JustAteApple: new EventPoint
}
eatApple() {
FireEvent(new Hedgehog.Event.JustAteApple);
}
}

Though, in the field, apart from hedgehogs, we may have a researcher that has to observe hedgehogs and take notes. This is a good example of how the subscription to events works in CORE — it happens statically, not in a common dynamic way. That’s why there’s no need to write additional code that will add the subscription.

HedgehogObserver = {
recordWhatHedgehogAte() {
CatchEvent(Hedgehog.Event.JustAteApple)
// to do smth
}
}

Also, one method can statically subscribe one object to more than one events:

HedgehogObserver = {
recordWhatHedgehogAte() {
var event = CatchEvent(
Hedgehog.Event.JustAteApple,
Hedgehog.Event.JustAteBanana,
Hedgehog.Event.JustAteKiwi
);
    if( event instanceof Hedgehog.Event.JustAteKiwi) {
// to do smth
}
  }
}
  • Requests

You can’t do everything by yourself (for example, something can be not the Object’s responsibility) and so are our hedgehogs and their observers (Objects). In this case, their fellows (other Objects) can help them out by means of Requests. So when the Object is unable to perform any activity, it sends a request for help. The important note: instead of calling method on a specific Object (which is usual Object-Oriented design style), it calls any Object that can do that (actually, the call goes to the Context where it can be caught by an available Object), specifying the caller’s object instead.

So if one of our observers needs a substitution, he has to shout for help in this way:

HedgehogObserver = {
askForHelpAndGoHome() {
FireRequest(
new HedgehogObserver.Request.TakeMyShiftPlease()
)
// go home
}
}
// handle it like that
AnotherHedgehogObserver = {
goToTheFieldAndStartObserve() {
CatchRequest(HedgehogObserver.Request.TakeMyShiftPlease);
// go to the field and start observe
}
}
// or even like that
ObserverStation = {
sendNewObserverToTheField() {
CatchRequest(HedgehogObserver.Request.TakeMyShiftPlease);
// get an available observer and send to the field
}
}

We can easily treat this message as the business logic message “Hedgehog Observer needs a shift”, and another part of system will know what to do with that.

Imagine how easy is to read and maintain such code, because your reading context contains both “What we do?” and “When we do that?” in one place, and you don’t need to look through the code recalling where the method was called.

You may wonder — is there anything you can relate CORE to? Sure thing!

I know that we always need to relate new concepts to something already familiar to us. That’s why for a better understanding of events and requests, let’s consider the most popular development concept — object-oriented programming. The difference between CORE and standard OOP is that the interface of an object in OOP is represented by methods while in CORE it exists in the form of events and requests.

What else? Well, CORE definitely has similarities with other programming concepts, such as:

  • automata-based programming (switching between CORE’s methods is an implementation of state machine);
  • actor model (each Object in CORE is an actor with its own state);
  • system of signals and slots (Requests and Events in CORE remind sending signals that can be received by other widgets).

Real-world case

Of course, many things sound cool in theory, though in practice far not all of them are so. But here’s the thing — my colleague and I have heavily tested CORE’s coolness on many real-world projects before even telling anyone about it.

As an example, imagine you have thousands of lines of code of video player for Smart TV, which you need to rewrite and adapt to another system. But instead of going the same way developers did (which could have resulted in at least a week of work), you decide to move backward — from the logic of the end product to its code implementation. And — voila — you manage to rewrite all those thousands of code during one day!

However, it’s not just about the time that we managed to cut with the help of CORE — but also, the new player was not more than few hundreds of lines of code long (thanks to CORE’s declarative nature) and functioned better than the original one (bugless and easy in maintenance).

Got it! So how can I use it?

Here is how I see the most optimal path:

  1. define your Objects and their responsibilities;
  2. list Events that happen to each Object;
  3. list Methods of each Object (you can think of them in terms of actions the Object takes)
  4. figure out which Methods are to be called automatically when particular Events happen
  5. if some Objects need the help of other Objects, add Requests and Methods that will handle them;
  6. implement technically

I have frameworks for JS and PHP which I can share, but the pattern can be easily implemented in any other common language — Python, C, C++, Go, etc.

Conclusion, or when do we need CORE?

We have seen where the idea of CORE came from, examples of how to apply, and now it’s time to draw the line under the first encounter with CORE. So which cases are the most appropriate for using CORE? Use it in the cases when:

  • you need to create an architecture from scratch
  • you have lots of bugs and overly complicated code (you can rewrite the part of your system using CORE)
  • when the logic becomes messy (it’s relevant at both planning and implementation parts)
  • it’s difficult to maintain existing code and you need to divide it into modules
  • you have to implement multi-language system design or the logic of a distributed system
  • in both server- and client- side programming, and even low level system programming

Just a few words about me (and, perhaps, about you too)

I’m a developer and an architect of high-performance systems. Designing of frameworks and APIs is the part of my work, and actually my hobby. :-)

CORE is the principle approach to designing system architecture I have been using for the past few years. I believe that CORE can help write better code and create quality products. And it’s not just a logical assumption, but the result of real experience of working on real projects in different teams.That’s why I would like to draw in the development of CORE as many IT enthusiasts (developers, QA testers, project managers, architects, etc.) of open source projects as possible. Guys, you can find information about the help that is needed here.

Also, you can support this project by making donations. With such help, I will be able to devote more time to its development, and porting to the different programming languages.

If you’re just wondering whether CORE suits your project’s needs, or you want to join development, feel free to contact me by email: okneigres@gmail.com

Finally, here are some links to continue the exploration of CORE:

https://github.com/extremeprog-com/core

Topics of interest

More Related Stories