
Microservices and the First Law of Distributed Objects - resca79
http://martinfowler.com/articles/distributed-objects-microservices.html
======
PaulHoule
The real strength of micro services is physical isolation more so than logical
isolation. That is, there is an 80/20 rule as to scaling.

For instance, there is probably one function of your system that is
responsible for a huge amount of work. If everything is in one database (say
SQL, mongo,...) You have a complex system that is hard to scale. If you split
the heavy load out, you might find the scaling problems vanish (because the
high load no longer has the burden of excess data) and even if there is still
a problem it is much easier to optimize and scale a system that does just one
thing.

The most disturbing thing about microservice enthusiasts is that they
immediately jump to: oh, we can write these services and clients in Cold
Fusion, Ruby, COBOL, Scala, Clojure, PHP and even when we write them, the
great thing is "WE DON'T HAVE TO SHARE ANY INFRASTRUCTURE!"

That's bougus to the Nth degree because a lot of the BS involved with
distributed systems has to do with boring things like serialization, logging,
service management, etc.

I think you still want to use the same language, same serialization libraries,
management practices, etc. across all of these services otherwise you are
going to get eaten alive dealing with boring but essential problems.

~~~
superuser2
Docker is a promising building block for solving logging and service
management and that's something I've been playing with for a while. You stream
your logs to your container's stdout/stderr and can pick them up from the
Docker REST API. You can also hit the Docker REST APIs across your hosts to
discover the IP addresses and ports of the containers running various images.
None of this is "solved" yet but it's getting there.

Serialization... depends, but you can get pretty far with JSON over HTTP.

So I guess you do need your microservices to be uniform in some respects, but
the consistency of "every microservice is a Docker container exposing a REST
API that speaks JSON and streams logs to stdout" would seem to be enough.

It's easy to build that in several languages... if Rails makes sense, use
Rails. If Sinatra makes sense, use Sinatra. If Java makes sense, use Java.

You could run into trouble if you start using languages that only one or two
of your staff members know, but it does allow for some flexibility (i.e. just
because certain parts of the application are enterprisey doesn't mean
everything has to be.)

~~~
PaulHoule
I definitely like the direction that Docker is going.

JSON over HTTP sucks. I mean, it's OK if user experience doesn't matter, but
if user experience does matter, then latency matters, and if latency matters
you find pretty quickly that serialization/deserialization overhead is a big
deal -- particularly in a microservices environment where an external request
is going to mean a large number of microservice requests happen, which means
data could be serialized/deserialized many times. If you're happy being in the
middle of the pack, go on with JSON, but if you are playing to win you need
something faster (preferably with a framework that means development as easy
or easier than JSON)

With multiple languages you don't just have the problem of training but you
have other complexity-multiplying problems.

For instance, one of the reasons why languages like Haskell are always a
bridesmaid and never a bride is that they lack a complete "standard library".
You need a urlencode() and decode function if you write web apps. If you write
your own you may be more concerned with making it tail recursive than making
it correct. I remember how the urldecode in Perl was busted for a decade and
never got fixed, and that's a mainstream language. As flawed as PHP is, it
took off because it had "good enough" implementations of everything web
developers need in the stdlib.

In a microservices system there tend to be functions similar to urlencode that
need to be replicated across the system, and if these are implemented over and
over again in different languages it is inevitable that some or all
implementations will be buggy. If you can centralize these things in one
library everybody depends upon, however, life is easy and happy.

~~~
kalkal
> With multiple languages you don't just have the problem of training but you
> have other complexity-multiplying problems.

> For instance, one of the reasons why languages like Haskell are always a
> bridesmaid and never a bride is that they lack a complete "standard
> library". You need a urlencode() and decode function if you write web apps.
> If you write your own you may be more concerned with making it tail
> recursive than making it correct. I remember how the urldecode in Perl was
> busted for a decade and never got fixed, and that's a mainstream language.
> As flawed as PHP is, it took off because it had "good enough"
> implementations of everything web developers need in the std lib.

I don't understand what you're saying here - are you arguing that we shouldn't
use languages that don't have web functionality built into the std lib? What
about web frameworks, e.g. Java + Spring?

~~~
PaulHoule
In Java you can find implementations of that stuff and will probably use it.
If there is anything wrong it is that there are too many different
implementations out there.

In the case of Haskell you ~might~ find implementation of that stuff but
you'll probably think you're too smart to have to reuse somebody else's code
and also be too smart to have to deal with the corner cases.

~~~
dragonwriter
> In the case of Haskell you ~might~ find implementation of that stuff but
> you'll probably think you're too smart to have to reuse somebody else's code
> and also be too smart to have to deal with the corner cases.

What is there besides extreme language bigotry to suggest that Haskell
programmers tend to think they are too smart to do either of these things?
Haskell programmers use other people's code all the time. And Haskell
programmers handle corner cases as much as any other programmers.

------
rubiquity
I find that the reason there is so much discussion about microservices and
scaling object oriented applications is due to the limitations of object
orientation in the first place. These same limitations are the reasons why
distribution and network/local transparency, something Martin Fowler states he
doesn't believe works, do work in functional programming languages but don't
work in OO languages.

It all boils down to OO programmers want their applications to be scalable and
maintainable. They have decided the way to do that is through modularity. But
we suck at enforcing modularity in a single code base. This has been proven
time and time again. Microservices are just a sneaky way of forcing that
modularity on ourselves. Instead of designing your system as a single ball of
mud (monolith), you'll design your system as a puddle of mud (microservices).

It is just entirely too hard to write good, modular OO programs. This is why
we hang onto every book, blog post and word the Object-Oriented Gods send down
to us. OO could be a great and amazing thing for certain domains of
programming. By all means, create monoliths and use Martin Fowler's Cookie
Cutter Scalability solution because it is simple. But if you find yourself
needing microservices, you're better off picking up a functional language
where modularity comes naturally.

~~~
PaulHoule
I disagree wholeheartedly.

If you read that post again you'll see that Fowler's admonition about "not
distributing objects" is really about "not distributing method calls", and
method calls aren't that different from function calls so far as his argument
is concerned in that (1) the latency of distributed function calls is
10^3-10^6 greater than local function calls, and (2) distributed function
calls often fail for reasons inherit to the network and not the application
itself.

I'll agree that functional programming languages have many virtues, in that
you can compose pure functions, run them in parallel and so forth, but
functional programming does not make the issues Fowler talks about go away.

~~~
dragonwriter
> If you read that post again you'll see that Fowler's admonition about "not
> distributing objects" is really about "not distributing method calls", and
> method calls aren't that different from function calls so far as his
> argument is concerned in that (1) the latency of distributed function calls
> is 10^3-10^6 greater than local function calls, and (2) distributed function
> calls often fail for reasons inherit to the network and not the application
> itself.

The expense of distributed method calls comes from the fact that you are
dealing with objects -- that is, containers of mutable state -- where you
either need to synchronize copies across the network (expensive in terms of
traffic/latency) or run all method calls on the place where the object lives
(likewise expensive in terms of latency).

Pure function calls don't depend on mutable state, and so don't have those
issues (further, to the extent that you do set up a system that requires a
remote call to execute them, once you've executed a given pure function with a
given set of arguments, you don't need to do it again, as the result is
cacheable and guaranteed good forever.) You still have to distribute the calls
that _actually_ depend on mutable state, but if your language starts out with
a clean distinction between pure functions and operations whose result might
depend on mutable state, you start out miles ahead when it comes to dealing
with distribution compared to working with a language which provides no
language level distinction between pure and impure code, requiring either
manual segregation or treat-everything-as-state-dependent-and-pay-the-cost
approaches.

~~~
bunderbunder
Practically speaking, function calls over a network are _by definition_
impure.

Any function that is capable of returning varying results for the same
arguments is impure. And a function call across the network could return
anything - a correct response, a corrupt response, no response - depending on
what the network's having for lunch at that moment. We do have clever tools
like the I/O monad, yes. But the I/O monad doesn't take I/O and make it pure;
it takes I/O and wraps it up in a clever facade that (usually) lets you
pretend that it's pure.

~~~
dragonwriter
Whether a function is logically pure or not precedes the decision to implement
via a network call and, in the event that a pure function is for some reason
implemented via a network call, shapes _how_ you implement it as a network
call (as it affects cacheability, etc.)

Obviously, the _medium_ of a network call is inherently impure -- but in the
same way that anything implemented in an any physical device is (including
internal computations in an electronic computer _without_ network
communication.)

~~~
bunderbunder
Technically, yes, you can follow that reductio all the way down the rabbit
hole. Practically speaking, though, it's not generally considered problematic
that there's no way to set a timeout before issuing the CALL instruction.

~~~
dllthomas
There _are_ ways to set a timeout before issuing (a particular) CALL
instruction. Occasionally these are important.

------
kentonv
This article is missing any mention of promise pipelining, which solves some
of the problems being discussed.

[http://kentonv.github.io/capnproto/rpc.html](http://kentonv.github.io/capnproto/rpc.html)

With promise pipelining, if you need to make two RPCs to the same server, and
the result of the first is going to be an input to the second, you can
actually do it in one network round trip. The trick is to send the server a
message saying "Hey, when you finish that first call, substitute the result
into the parameters of this second call".

With this, fine-grained calls no longer imply an enormous latency expense
compared to course-grained. Meanwhile, fine-grained APIs are cleaner and more
composable, as my link above describes.

It's unfortunate that CORBA gave distributed objects a bad name. Just like
object-oriented design within a program is more expressive than procedural
design, object-oriented network protocols are more expressive than the flat
protocols we tend to see today. I've been working with object-oriented
protocols a lot lately while using Cap'n Proto to build sandstorm.io, and I've
surprised even myself at how much more elegantly I can express complex
interactions.

CORBA only messed up in trying to make remote objects look the same as local
objects. Everyone now agrees that was a terrible mistake. But making
distributed objects work does not in any way require making them look exactly
like local objects. Calls to a Cap'n Proto object look quite different from
local calls, because you need to be aware of the network issues implied by the
call. But I've found that the same higher-level OO design principles you might
use locally translate remarkably well to Cap'n Proto interfaces.

~~~
a8da6b0c91d
I virtually never see metion of ZeroC ICE and OpenSplice/DDS. These seem like
very well thought out and solid solutions for making distributed systems.
Everybody seems to go on about thrift and REST and ZeroMQ and so forth, when I
would much rather use DDS or ICE. The other approaches seem to amount to
homebrewing your own approximation of these technologies much of the time.

~~~
kentonv
ICE does not support promise pipelining, though.

[http://kentonv.github.io/capnproto/news/2013-12-13-promise-p...](http://kentonv.github.io/capnproto/news/2013-12-13-promise-
pipelining-capnproto-vs-ice.html)

I haven't looked at DDS.

------
AdrianRossouw
This is the spirit of microservices to me :

[https://news.ycombinator.com/item?id=7874317](https://news.ycombinator.com/item?id=7874317)

"Instead of pretending everything is a local function even over the network
..., what if we did it the other way around?

Pretend your components are communicating over a network even when they
aren't?"

\-- Solomon Hykes (of Docker fame) on LibChan

To me, it's pretty much anti-OO, and that's why I find it refreshing.

~~~
CMCDragonkai
Isn't that kind of like erlang?

~~~
AdrianRossouw
it's very much like that. and erlang has proved that it's a valid approach.

~~~
angersock
Yep. All the distributed systems I've worked with devolve into a bad
reimplementation of part of Erlang.

------
munificent
> Given this uncertainty, the most important thing a writer like myself can do
> is to communicate as clearly as I can the lessons we think we've learned,
> even if they are contradictory. Readers will make their own decisions, it is
> our job as writers to make sure those decisions are well-informed ones,
> whichever side of the architectural line they fall.

This is a really insightful description of the role of someone documenting
software architecture.

------
pjungwir
I really admire the humility of his wait-and-see attitude, especially since he
is trying to keep an open mind against his instincts.

This is a bit OT, but it seems like Angular apps have similar problems to
distributed objects, where you can wind up making lots of network calls to
retrieve one of these, all of those, etc. I'm curious what advice people have
about that.

~~~
lmm
> This is a bit OT, but it seems like Angular apps have similar problems to
> distributed objects, where you can wind up making lots of network calls to
> retrieve one of these, all of those, etc. I'm curious what advice people
> have about that.

You need to make a distinction between remote and local calls. You need
something that looks a bit like function calls - not a several-line ceremony
for each call - but also that clearly isn't the same thing as a function call,
and you need a way to make sure you don't get the two mixed up. You need
(whisper it) monads.

I've heard that new versions of Angular will have Dart as a primary platform?
Maybe they can make a type-level distinction between remote and local calls in
that language?

------
rdtsc
> I worry that this pushes complexity into the interconnections between
> services,

I guess the instinctive answer is to well let someone else solve the problem.
Grab something existing / standard (REST API + rest client, RabbitMQ +
msgpack) or something similar.

What it still doesn't save you from is managing basic distributed systems
issues - network partitioning, timeout, asynchronous starts and stops. Maybe
it is better because by building this distribution into the core of the system
and not trying to abstract it away behind an API (like the author says) it
forces you to deal with them explicitly.

Overall I still haven't decided if microservices is just one of those buzz
words invented because the old ones (Object Oriented, SOA, etc) have gotten
old and don't bring in consulting revenue anymore.

~~~
AdrianRossouw
I think microservices is a new evolution of an old idea, that is growing to
term in an environment that could allow it to blossom.

As I posted elsewhere here, microservices are also explicit about being remote
calls, and are built with the idea of assuming all your calls are always
remote.

For instance, Docker is already built using a microservice architecture, and
they are standardising that as a project called LibChan :
[https://github.com/docker/libchan](https://github.com/docker/libchan)

It's basically msgpack over SPDY+TLS, all the way down the stack.

disclaimer: I am one of the co-founders of jsChan, the node.js/javascript port
of libchan.

[https://github.com/GraftJS/jschan](https://github.com/GraftJS/jschan)

------
oldpond
I believe they are grasping in the right direction, but what they really need
to do is let go of the attachment to object oriented development. I don't mean
to start the flame war, but if you insist that the architecture reflects the
intent of the application, then why choose OO? If the architecture requires
messages and buses and adapters, etc. what does OO bring to the table? Why do
I think I need distributed objects? Am I choosing micro-services just because
I want to try to get distributed objects working again?

It's not a language issue either. At this level we are talking about
frameworks, models, domains, contracts, protocols, etc. This layer is not
language dependent, although some languages are better designed to build
frameworks that support these intents.

A classic example of how these assumptions creep into your designs is seen in
the first chapter of Head First Design Patterns where they discuss at length
how to create the perfect object model for a duck computer game. When I read
that the first first think that came to my mind was, "Wait a minute, you are
designing a computer game! Everything on the screen is a sprite. Sprites are
moved around the screen by their coordinates once per game loop. How does the
perfect duck object model help me here?"

Sounds very much like a hammer looking for a nail to me.

[edit] fixed a few typos

------
ilaksh
High level knowledge representation like description logics can make these
types of discussions obsolete if they are applied to information systems
holistically. Describing relationships between data and describing processes
themselves using a common machine-processable language would allow the
plumbing to be generated automatically and could even enable systems to
automatically be converted from course grained message types to finer grained
ones and vice versa.

------
crack_one_out
Oh for the love of <insert deity here> no! This will now be printed and stuck
onto the wall by the astronaut architects where we work, who blindly parrot
MFs words like gospel, without any understanding.

Software used to be shit, but at least fun, then we had software architects,
and now software is just shit.

Sigh.

