Ok I can’t help but be reminded of this
@chadfowler
The older I get, the more I realize the biggest problem to solve in tech is to get people to stop making things harder than
they have to be.
… follow you so far but then …
I think you may be getting a bit hung up on the whole “reflection” business (always seemed a bit “smart aleck” to me anyway - if you want your code to be data use a Lisp) - and then as a result be possibly overcomplicating things.
Back in the dark ages when I was working in C this kind of problem was solved quite simply by:
- Beginning the message with a fixed width discriminator field - i.e. the bit pattern in here identified the kind of message that it was. If you have lots of different types of messages you needed a wider field.
- The second fixed width field would identify the total size of the message. While bytes as a unit would seem natural you could actually chose whatever unit you want - just be consistent.
So the size makes it possible to grab the entire message regardless of what the message actually contains and the discriminator is then used to select the appropriate dispatch function. The entire message is then simply handed to the correct dispatch function which
a) knows the internal structure of the message to extract the necessary information
b) knows where to dispatch this information to, in order to get it processed
Given the extensive support of binaries in Erlang I’m sure that there is plenty of Erlang code that operates in exactly the same way and that Elixir is quite capable of handling it. Also if the discriminators can be legally used as Map keys - you could load a map at runtime with discriminator/dispatch function pairs to select the correct dispatch function without writing a huge static case expression.
This is probably the biggest issue that you are facing - it sounds to me like your messages are being constructed “auto-magically” by some wizard based on some data structures as they exist in some source language. In the web services world this is called code first/contract last development - and it is bad news. Contract first/code last is always the way to go even if it is less convenient at first - design the messages first and independent of the implementation languages that may be used. Typically that is the best way to ensure that the messages are simply constructed and can be read in any implementation language.
To me this sounds you are still suffering from a “synchronous mindset” and because of it your design is having problems. Move towards an asynchronous processing model:
-
One part of the system is responsible for processing incoming messages. It’s only responsibility is to make sure that the incoming data is routed to the place that needs to process it. It is not concerned at all with any potential responses.
-
Once the data is processed it is routed to an entirely different part of the system which sends off the results (back?)
Essentially you need to start following Scott Wlaschin’s advice (“Thinking Functionally”) and move away from the request/response model of using functions and processes and think more in terms of input/output and “railway oriented” programming.


















