Vault - a lightweight process-scoped global data storage with immutability guarantees

I’ve found that a lot of judgement of how to decide these kinds of things, for edge cases anyways, is very aesthetic – there’s almost always a taste or style that is very easily grokkable, for me and the rest of the team (if there is a team).

I’ve been using something very similar to your grades:

  1. I use Phoenix in a lot of my projects and sticking some info in the Plug.Conn.t values is perfectly fine, e.g. the current user (Ecto schema struct). That seems very much like what Vault is intended for, but without needing to explicitly thread the context value wherever it’s needed in a call tree.
  2. I actually don’t like params arguments myself. I DO use opts liberally, and (try to) type every option explicitly too, and that’s very similar. What I was gesturing at is more like ‘a Phoenix assigns but more general’ and the reason that I mostly use these types at the ‘upper-stream’ of call trees is that’s a way to organize the state for the overall ‘project’ that the call tree is attempting. It’s also partly a way to ‘encourage’ good DB use, i.e. load ‘everything’ up front and then pass that, or pieces thereof, to the rest of the call tree. I work with lots of ‘legacy code’ and ‘just fetch it from the DB’ is all too common of a pattern, and all too commonly a source of performance issues. So think ProcessPaymentInfo instead of params, tho maybe I’m imagining more of a difference with what you meant. I’ve found tho that lots of functions near the top or in the middle of call trees can be very sensibly written to accept, e.g. ProcessPaymentInfo, or a collection of similar types. Typically almost all of the functions that accept, e.g. ProcessPaymentInfo, values would be in the same single module, or maybe a small set of modules (usually with a single ‘top-level facade’ module that all the other code is intended to use).
  3. Yes, good ol’ function arguments are the generic ideal! The functions at the lower-stream of call trees should be cute little pure functions with a handful of regular ol’ arguments. All of the calls made by the ProcessPaymentInfo functions, to functions outside of their module(s), would pull out ‘sub values’ from the ProcessPaymentInfo value(s) and pass them as ‘regular’ function arguments.

What are you using Vault for yourself? What are some other people using it for?

1 Like

I’m currently using it to store the current user from Absinthe request context, normal REST API requests, and inside phoenix channels. So that when resolving queries/mutations I don’t need to pass it down the flow. This allowed me to add logging (and other handling too) without changing function arities, and actually without modifying the existing code at all. I just now have the user everywhere, and it’s really cool.

2 Likes

This functionality has been extracted to NimbleOwnership v1.0.1 — Documentation a year ago or like. Basically, I believe NimbleOwnership somewhat covers the functionality of managing resources across processes.

2 Likes

Yeah, this approach is common. I didn’t use the library (because the whole logic can be expressed in like 100 lines of Elixir anyways, haha), but I used this approach in Repatch library of mine

2 Likes