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