Master Hexagonal Architecture in Rust
The guide on hexagonal architecture in Rust promotes scalable applications by emphasizing separation of concerns, introducing the repository pattern, and providing practical examples for effective implementation and incremental learning.
Read original articleThis guide on hexagonal architecture in Rust aims to help developers create scalable and maintainable applications. It begins by illustrating the pitfalls of a poorly structured Rust application, which often leads to hard dependencies and tightly coupled code that is difficult to test and refactor. The guide emphasizes the importance of separating concerns, advocating for a design where different components of the application interact through well-defined interfaces. By adopting hexagonal architecture, developers can encapsulate dependencies, making it easier to swap out components like databases or HTTP servers without extensive rewrites. The guide also introduces the repository pattern, which helps abstract data storage, allowing the application to remain agnostic of the underlying database technology. Throughout the tutorial, practical examples are provided, including a blogging engine built with the axum web server, demonstrating how to implement these architectural principles effectively. The author encourages readers to engage with the material incrementally, digesting each section before moving on to the next, ensuring a thorough understanding of hexagonal architecture's benefits and trade-offs in Rust development.
- Hexagonal architecture enhances maintainability and scalability in Rust applications.
- The guide emphasizes the separation of concerns to avoid tightly coupled code.
- The repository pattern is introduced to abstract data storage and improve flexibility.
- Practical examples, including a blogging engine, illustrate the implementation of these principles.
- The tutorial encourages incremental learning and application of concepts.
Related
Rust for Rustaceans
"Rust for Rustaceans" by Jon Gjengset is a book for developers with basic Rust knowledge, focusing on advanced topics, codebases, and programming challenges, praised for clarity but noted for its density.
Don't write Rust like it's Java
The author discusses transitioning from Java to Rust, highlighting Rust's type safety, differences in traits and interfaces, complexities of ownership, and the importance of embracing Rust's unique features for effective programming.
From Julia to Rust
The article outlines the author's transition from Julia to Rust, highlighting Rust's memory safety features, design philosophies, and providing resources for learning, while comparing code examples to illustrate syntax differences.
Rust's Ugly Syntax (2023)
The blog post addresses complaints about Rust's syntax, attributing them to misunderstandings of its semantics. It suggests simplifying semantics for readability while maintaining performance and safety features.
Writing an OS in Rust
The GitHub repository for the _Writing an OS in Rust_ series offers structured source code for each blog post, covering key OS development topics in Rust, with links to the latest content.
Attempting these design patterns is a common part of getting over OOP when new to Rust. The result: over-abstracted, verbose, unmaintainable C++/Java written as Rust. Every layer of indirection ossifies the underlying concrete implementations. The abstractions inevitably leak, and project velocity declines.
I have seen the same types and logic literally copied into three different repositories in the name of separation of concerns.
Luckily people usually get over this phase of their Rust career after a couple of failures.
If you’d like to skip that part, here are a few rules:
1. Always start with concrete types. Don’t abstract until you have at least two, preferably three, concrete implementations.
2. Separation of concerns is a myth.
3. K.I.S.S.
At $work we’re slowly ripping out a similar system in favour of handlers calling database functions directly, such that they can put transactions in the right place across more complicated sets of queries. Code is simpler, data integrity is better.
I'm all for the unit testing argument, but in an active SaaS business I've never seen the hypothetical database change where a well architected app makes it smooth. I have certainly moved databases before, but the performance and semantics changes dwarf the call sites that need updating. Especially in Rust, where refactoring is quite straightforward due to the type system.
Mixed feelings about this.
“Oh our http handler knows about the db”
Ok? Its job here is more or less “be a conduit to the db with some extra logic”.
It “knows about a lot of things” but it’s also exceedingly clear what’s going on.
The main function is fine. I’ll take a “fat” main function over something that obscures what it’s actually doing behind a dozen layers of “abstraction”. Nothing like trying to isolate a component when you’re fighting an outage and nobody can figure out which of the 16 abstract-adapters or service-abstracted launched the misbehaving task.
The original code might be “messy” but it’s at least _obvious_ what it’s doing, and it can pretty clearly be pulled apart into separate logic and IO components when we need to.
This all just feels a bit…over engineered, to say nothing of the insulting tone towards the other learning resource.
Tell me the thing that's in the headline first. I'm not going to read your article if you don't do this. It's not that I'm not intellectually curious, it's that I don't like being messed around.
Never needed to. Premature optimization.
> We have the same issue with the database
What issue?? Cross that bridge when you get to it...
> To change your database client – not even to change the kind of database, just the code that calls it – you'd have to rip out this hard dependency from every corner of your application.
I really doubt this is such a big deal... Bit of ctrl+f for that module and paste the new one. All database libraries have a "connect()" and a "query()".
I'm so far convinced that this article is written for people who have too much time to waste on things that will probably never happen just so they get to feel smart and good about something that's not even visible.
Imagine if we built bridges this way: yes, right now we have settled on a stone masonry design, but what if we want to move to steel trusses in the future???
Why can software engineers not accept that it is unreasonable to expect one single human creation to last forever and be amendable to all of our future needs? Cross the bridge when you get to it. Don't try to solve future problems if you're not 100% certain you're going to have them. And if you _are_ certain, just do it right the first time, rather than leaving an opening for a future person to "easily" fix it up. And sometimes? A new bridge has to be built and the old one torn down.
Now he's not here anymore, and posts in his linkedin about simplicity and how people overcomplicate things in software so much. This shows me that you should be careful when taking advice from other engineers, they learn and move on from what was previously a "best practice", while you might get stuck thinking it's worthy it because "that one very good engineer said it was how it should be done".
If you want to test, test at the integration layer using testcontainers[0] or something. Not everything has to resemble a Spring style dependency injection/inversion of control.
Hell is full of single-implementation abstractions.
Total nitpick: For `CreateAuthorError::Duplicate`, I would return a 409 Conflict, not a 422 Unprocessable Entity. When I see a 422 I think utf-8 encoding error or maybe some bad json, not a duplicate key in a database.
The two main use cases are routing frameworks and tests.
Axum already has this, but it is married to HTTP a bit too much.
Hexagonal architecture brings order to chaos and flexibility to fragile programs by making it easy to create modular applications where connections to the outside world always adhere to the most important API of all: your business domain.
okay.And what's the point again? To make it so that you can switch out Postgres for MongoDB later? To make your web app now accessible over XMPP? It feels like a lot of work to enable changes that just don't happen very often. And if you wrote your app with Postgres, the data access patterns will not look very idiomatic in Mongo.
I think X11 in *nix land is an interesting example of what I mean. X11's client-server architecture allows a window to be "powered" by a remote machine as well as local. But it's dog slow. Straight up streaming the entire desktop frame by frame over VNC is usually smoother than using X11 over the network. I think we just haven't reached a point yet where we can develop good apps without thinking about the database or the delivery mechanism. (I know X11 isn't exactly new, but even with decades of advancements in hardware, X11 over the internet still loses to VNC)
Related
Rust for Rustaceans
"Rust for Rustaceans" by Jon Gjengset is a book for developers with basic Rust knowledge, focusing on advanced topics, codebases, and programming challenges, praised for clarity but noted for its density.
Don't write Rust like it's Java
The author discusses transitioning from Java to Rust, highlighting Rust's type safety, differences in traits and interfaces, complexities of ownership, and the importance of embracing Rust's unique features for effective programming.
From Julia to Rust
The article outlines the author's transition from Julia to Rust, highlighting Rust's memory safety features, design philosophies, and providing resources for learning, while comparing code examples to illustrate syntax differences.
Rust's Ugly Syntax (2023)
The blog post addresses complaints about Rust's syntax, attributing them to misunderstandings of its semantics. It suggests simplifying semantics for readability while maintaining performance and safety features.
Writing an OS in Rust
The GitHub repository for the _Writing an OS in Rust_ series offers structured source code for each blog post, covering key OS development topics in Rust, with links to the latest content.