Okay, let's unpack this, because you know, there's a very specific kind of anxiety that comes with modern software development.
I know what you're talking about.
I feel like we have all been sold this micro services dream for years now. You know the pitch kill the monolith, break everything into these sleek, tiny, independent services.
It sounds like utopia.
It sounds perfect. You scale what you need, you deploy whenever you want. Everyone's happy.
On paper, it is the perfect architecture. It promises agility.
Resilience right on paper. But then you know, reality hits, and suddenly you aren't just writing business logic anymore. You aren't just selling cookies or processing payments. Suddenly you have to become a distributed systems engineer. Just to write a simple shopping cart app. You're dealing with retries and certificate rotation and state management and service discovery. It's like buying a Ferrari but realizing you have to pave the highway yourself before you can drive it.
That is a painfully accurate analogy. Ustry actually calls these the fallacies of distributed computing. We assume the network is reliable, latency is zero, bandwidth is infinite, which they never are they're not, and handling that complexity usually means writing thousands of lines of plumbing code that has absolutely nothing to do with the actual value your software provides.
And that is exactly where today's deep dive comes in. We are looking at a solution that claims to fix this plumbing nightmare, Dapper or the distributed application run time. We're basing this discussion on the book Practical micro Services with DAPRA and dot Net by David Bidden, and.
This book is essentially a field guide. It doesn't just theorize it. It walks you through the gritty details of building a real system and the core argument but an actually, as your CTO, Mark Orsinovitch, who wrote the forward, makes is that developers should stop writing this plumbing code entirely.
Orsinovich uses a metaphor that I really want to dig into. Right at the start. He calls Dappra a butler for your code.
It's such a great image. Think about it. If you're a busy executive, you want to focus on strategy, the what you don't want to worry about how to mail a letter or how to file a receipt or who to call to fix the sink.
You want someone to just handle it.
You want a butler to handle the how In this context, your code focuses on the business logic, and Dapper is the butler handling all those messy infrastructure tasks.
So instead of me having to hardcode, Hey, connect to Rettus at this specific IP.
Address, authenticate with this token, and.
Retry three times. If it times out, I just tell the butler save this and I go back to work.
That is the promise. Yeah, But to understand how it does that without being magic, because there's no magic in software, we have to look at the architecture. Right. The part isn't a library you can pile into your code. It uses a sidecar pattern.
Okay, this is a term that gets thrown around a lot in cloud native circles. Sidecar. Let's strip away the jargon. What does this look like practically?
Okay, visualize your application. Let's say it's a microservice in dot Net or Python or go. Picture that app as a person sitting at a desk doing work. In a traditional setup, that person has to have a phone, a fax machine, a rolodex, and a massive instruction manual on their desk just to talk to the outside world.
And they have to know how to operate all of them perfectly exactly.
Now, with Dapper, you add a sidecar. This is a separate process, a personal assistant sitting at a desk right next to them.
So they're practically touching elbows right.
If your app is in Kubernetes, it's a container in the same pod. If you're on a laptop, it's a process running alongside. Now, if your application needs to talk to another service, it doesn't shat across the office. It leans over and whispers to the assistant, get this message to shipping. The assistant the Dapper sidecar is the one that looks up poor shipping is handles security, encrypts the message and make sure it gets there.
That distinction is crucial because it implies language independence. If I'm whispering to my assistant, I can whisper in English or Spanish or even binary.
Precisely, this is the any language, any framework, anywhere philosophy. Bedin emphasizes your app talks to its DApp sidecar using standard HTTP or gRPC, just over local host. It's incredibly lightweight, so I could have.
A legacy Java app talking to a brand new Go service.
It depri acts as the universal translator or maybe diplomat between them.
Okay, but I have to play Devil's advocate here. I'm a developer. I hear extra process and network hop. Adding a hop sending traffic to a sidecar before it goes to the network. Doesn't that add latency? Are we trading performance for convenience?
It's a valid concern, and Bedan addresses it technically. Yes, there is overhead. We're usually talking microseconds or submilisecond times for that local hop. Okay, but consider the counter argument. Can you write a better, more optimized retry loop, connection pool, and security handshake in your custom code than the engineers who built the run time?
Probably not. I'd likely just write a while true loop and hope for the best.
Exactly, And here's the kicker dapper. Sidecars communicate with each other using gRPC. Oh interesting, So even if your app speaks simple HTTP to its sidecar, the sidecar upgrades that to gRPC with proto buff serialization when it talks to other sidecars over the network.
So you might actually see better performance.
You might because the wire protocol is so much more efficient than a poorly written direct HTDP client. You're outsourcing the optimization to the runtime.
That's a fair point. Okay, let's get practical. The book uses a really specific and honestly dangerous for me. Example, we are building an e commerce site for biscotti booty.
Mobile wanning ugly but good cookies, a classic Italian tree.
I am getting hungry just saying it. So we have this cookie store, and Benin breaks it down into a classic micro service triad and order service to take money, a reservation service to check inventory.
And a shipping service to get the cookies out the door.
Right, it's the Hello world of distributed systems.
But it's perfect because it forces us to deal with the three bigges headaches, service invocation, state management, and event driven messaging.
Let's tackle the first one, service invocation. I'm the order service. I have a customer who wants a dozen biscotties. I need to ask the reservation service do we have twelve cookies.
In the battle days, you might hardcosee the reservation service's IP address or DNS name into your configuration.
And then it moves.
It moves, the IP changes, your app crashes. It's fragile. Extremely With DAP, your order service just calls local host and says invoke the method. Check inventory on the application named reservation service.
So I'm addressing it by a logical name, not a physical location.
Correct. The dapper sidecar looks up that name. If you're running locally on your laptop, it uses mDNS. If you're in Kubernetes, it uses the cluster DNS. Okay, it finds the target. But then it does something really slick. It automatically applies MTLs mutual TLS.
Encryption, so the traffic is encrypted and both sides verify each other's identity without you.
Writing a single line of security code. It's zero trust security right out of the box. And if the reservation service is restarting or the network blips it failed, Dapper automatically retries the request with an exponential backoff policy.
That's the butler hiding the mess. I don't see the network failure. I just see a slightly delayed success exactly.
But now we run into the second headache. Micro services are supposed to be stateless, right.
If a container crashes, it forgets everything in its memory.
But we are selling cookies. We need to remember that we only have fifty left. If we forget that we sell cookies, we.
Don't have and nobody wants a phantom cookie.
This brings us to Dapper State management building block. And this is the part of the book where the plug ability aspect really click for me.
Walk us through that, because usually picking a database is like a marriage. You pick reddis, you install the redssdk, you write Reddus commands.
And divorce is expensive. Very it is, Yeah, but dappra treats state as a generic key value store. Your application sends adjacent payload like cookie inventory fifty to the Dapper sidecar and says save this. You don't tell it where.
Wait, so how does it know where to put it?
Configuration not code. You have a yamal file, a component file that defines the state store. On your developer laptop, that yamal file points to a reddis container running and Docker. Okay, But when you deploy a production you swamp that yamal file to point to Azure Cosmos dB or AWS Dynamo GB.
And I don't change my c sharp or Python code.
Not a single line. The application logic is completely decoupled from the infrastructure implementation.
That's huge for portability. But I have to ask about the nuance here, because databases aren't just generic buckets. What happens if two people try to buy the last cookie at the exact same millisecond. The last Cannoli problem.
A classic race condition. Benden spends time on this because it's where the abstraction could get leaky if you aren't careful. Dapper handles this using E tags.
Explain that for the expert.
Think of an E tag like a version number on a document. When I check the inventory, Daker tells me there are fifty cookies and this is version one.
Okay.
If I want to buy ten, I tell Dapper update inventory to forty, but only if it's still version one, and if.
Someone else sneaked in and bought a cookie while I was thinking.
The database is now at version two. Dapper sees my request with version one, realizes it's outdated, and rejects the right. This is first right wins concurrency. I see that Dapper exposes this complexity in a standardized way, so you can handle it properly, regardless of whether the underlying database is RETTIS or SQL.
So it prevents me from accidentally overwriting someone else's purchase. That's critical. What about consistency? I know in distributed systems there's this fight between strong and eventual consistency.
Yes, and Dapper lets you choose. Do you need strong consistency where all database replicas must confirm the right before we move.
On safer but slower.
Exactly, or do you want eventual consistency where we just need one acknowledgment and trust it'll propagate. Dapper gives you a dial to turn that logic again via the API calls, not by changing your database driver.
Okay, so we've ordered the cookies, We've reserved them. Now we need to ship them in a monolith. I'd just call the shipping class. But here we want to decouple this. I don't want the order service to hang while the warehouse printer is warming up exactly.
Shipping is an asynchronous process. The user wants to see order confirmed immediately. They don't care about the back end logistics yet. This is where Beddin introduces the publish and subscribe or pub sub pattern.
So the order service effectively picks up a megaphone and shots order one twenty three is ready, and it doesn't.
Care who is listening. It publishes that event to a topic. Yeah, the shipping service subscribes to that topic. Maybe an email service subscribes to maybe a loyalty point service.
They all react independently.
And sitting in the middle handling the megaphone is Dapper again I'm guessing so Dapper and a message broker. Just like with the database, Dapper abstracts the broker. You could use Redis locally, then switch to as your service bus or Rabbit MQ in production. But there's a really cool scalability feature here called competing consumers.
Competing consumers that sounds like Black Friday shoppers. What does that mean here?
Imagine our cookie shop goes viral. Suddenly you have ten thousand orders coming in per minute. One instance of the shipping service can't handle printing that many labels, so you spin up ten instances of the shipping service.
Okay, but if I shot order ready, won't all ten of them try to ship the same package.
That would be a disaster. But Dapper is smart enough to act as a load balancer for these messages. It uses the consumer group concept. It ensures that order A goes to instance one, order B goes to instance two.
It spreads the workout automatically.
Correct and if Instance one crashes while processing order A, Dapper detects the failure. Maybe the acknowledgment never came back and redelivers it to instance too.
That's the distributed systems engineer stuff. I really don't want to write it is, I've done it.
I don't recommend it. Writing a lease management system for message locking is a nightmare, and DAP just gives it to you for free.
It changes your whole mindset.
It does you stop thinking about calling service B and start thinking about publishing an event that something happened. It makes your system much more flexible.
I want to pivot to the developer experience because honestly, this all sounds great architecturally, but running a distributed system on my laptop usually sucks.
It's a major friction point.
I need to spin up five different terminals, manage ports, work about Docker containers.
It's just friction for sure. In the past, people would just build a monolith locally and hope it worked as micro services in production, which of course it never did. But the book dedicates a whole chapter to tooling, specifically the Dappa TAP extension for vs code and a tool called tie.
I was looking at the tasks do Jason examples in the book. It looks like you can script the whole environment startup.
You can. You can configure it so that when you hit debug in VS code, it launches your order service, your reservation service, spins up the dapper of sidecars for both, and even launches a local REDTIS container. You get a fully integrated debugging experience.
And can I set break points? Can I step through the code across services?
You can set brake points in your c shark code just like a monolith. But when the request leaves your service and goes to the sidecar, you lose it until it hits the next service.
Ah.
Right, This is where observability comes in.
Right, Because debugging a single process is easy, Debugging a request that bounces through five services is like hunting ghosts exactly.
Yeah, a user says my order failed and you have no idea if it died in the order service, the network, or the database. And typically to fix this, developers have to manually instrument their code. They had logging everywhere, entering function A, leaving function B.
It's tedious and it clutters up the code.
Yeah. But because dapper intercepts every request going in and out, it automatically generates distributed tracing data. It supports the zipkin protocol standard.
Zipcn is the tool that draws those waterfall timelines, right, yes.
Yeah, because the traffic flows through the sidecar. You get that map for free. You could open the zipken dashboard and see a visual timeline. Okay, the request hit the order service, spent five meters there, then went to reservation. Whoa look the database saf took five seconds.
You spot the bottleneck instantly.
It's invaluable. It can expect to the butler analogy. The butler keeps a log of every visitor and every message pasted. You don't have to write it down yourself.
You just asked the butler what happened with order one twenty three, and he hands you the log. That's the idea that sounds incredibly useful. So we've covered the architecture, the sidecars, state management, pub sub, the tooling. When you step back and look at the Biscotti Britty Mborney example as a whole, what is the synthesis. What's the big takeaway for me?
It comes down to two words, autonomy and evolution.
Okay.
In a traditional setup, your code is tightly coupled to your infrastructure choices. If you want to change from rabbit MQ to service bus, you are rewriting code to move from on prem to the cloud. Your rewriting code.
And tight coupling is the enemy of speed. It makes everyone afraid to touch anything.
Exactly daypre decouples the what from the how. It allows the infrastructure to evolve at a different speed than the application code. You can modernize your back end storage without frightening the developers who are just trying to sell cookies.
It standardizes the plumbing across the organization. You don't have one team implementing retries one way and another team doing it a different way.
And I think we need to end on a bit of a provocative thought here.
Hit me, what's the catch?
There is a risk we are creating a very comfortable abstraction layer. If DAPRA acts as this universal butler handling all the plumbing. Are we creating a generation of developers who don't understand how distributed systems actually work.
That's a valid fear. If I don't know how the sausage is made, do I know how to fix it when the machine jams?
Exactly? DAPPA hides the complexity, but it shouldn't hide the reality. You still need to understand that data isn't instantly consistent. You still need to understand that messages can arrive out of order. Right, Debra gives you a really fancy electric screwdriver, but you still need to know how to build the cabinet.
That is a great distinction. It's a tool for professionals, not a magic wand that removes the need for knowledge.
Well put, it amplifies capability, it doesn't replace understanding.
Well for anyone who wants to see the code behind the cookies. And frankly, the Dapper un commands are really satisfying to see in action. Davidybden's book Practical micro Services with Dapper and dot Net is the source. The examples are on GitHub and they are definitely worth the clone.
I highly recommend looking at the transition from the controller approach to the gRPC approach in his code. It really eliminates the performance benefits we discussed.
Thanks for diving deep with us today. Hopefully your next distributed system feels a little less like a headache and a little more like well, like cookies, ugly but good cookies exactly see on the next deep dive
