-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PoC - Absorbing views and params into Nexus #191
Conversation
cb1285f
to
01cb47c
Compare
01cb47c
to
56cc8f7
Compare
omicron-nexus/src/lib.rs
Outdated
mod saga_interface; | ||
mod sagas; | ||
pub mod views; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably don't need to be public, I just don't know how to make the tests that use these work otherwise
I do wanna give the caveat - which I think you're already aware of - we should only make this move if we can do "everything or nothing", because having the Nexus API split between the omicron-common That being said, assuming we could move everything from (Things I like)
(Questions/Concerns)
TL;DR: I like this direction, but I think good client generation + preserving the internal/external boundary will be a prereq. |
Good point, I forgot about that because Project is only external. Something about that distinction still rubs me the wrong way, but I can't put my finger on it. Maybe I've just seen too much Maybe we can go for a dir structure like this:
Params are the inputs to post and put requests and views are the responses. They can go in the same file (maybe they should be grouped by resource anyway), I just thought it was cute using
Yeah, looks like almost nothing is in there, which is funny. Good case for the client generation. |
b9e045b
to
f5166e1
Compare
Not sure I like my approach, but 3877675 is an attempt at bringing external/internal back. The weirdest part (good example of why these experiments are useful) is putting params in external. This weirdness is helping me see where your question above about params vs. views was coming from. On one hand params represent the expected shape of a request body, but they are currently also used as args for most of the create and update functions in I think this collapsing of the distinction between API params and db function params is going to make less sense as we go forward because the API params are going to stop mapping directly to the model properties. For example, right now on instance create we take memory in bytes and pass that straight through to the database. But whatever the outcome of RFD 190 is, we surely don't expect the public API to specify memory in bytes forever. Presumably we will take it in GiB or MiB and convert to bytes (assuming we don't store it as MiB in the DB). A more dramatic version of this is the API taking an instance type name as a string and converting that to the right ncpus and memory and whatever. In any case, the API instance create endpoint params end up distinct from the DB instance create function params and there has to be a translation step (which doesn't have to be formalized into a A rather maximalist design that goes along with the existing setup would be that every PUT and POST endpoint has a corresponding params struct representing its request body, and then the datastore functions all have their own structs that presumably line up with the db representation and can be passed more or less straight through. The scrappier lightweight version enabled by a query builder would be that the datastore functions don't have interfaces because datastore doesn't exist, and where we currently call it let instance = osagactx
.datastore()
.project_create_instance(
&instance_id?,
¶ms.project_id,
¶ms.create_params,
&runtime.into(),
) we call the query builder directly: let instance = models::Instance { blah blah blah };
diesel::insert_into(instances::table)
.values(&instance)
.on_conflict_do_nothing()
.execute(&*conn)?; I like this version better. Structs representing request params still exist, of course, but as they are only used for parsing request bodies, they don't have to be imported anywhere besides the entrypoints file. They could even be defined inline next to their http entrypoint functions. Some useful examples from crates.io
|
f5166e1
to
3877675
Compare
We can pick this sort of thing back up after #192 |
Here's one way it could look to move more of what's currently in common into Nexus, using
Project
and its create and update params as a demo. This is one of the few models where we don't have to choose between client gen and literal code sharing becauseProject
is not used by internal clients. Doing this piecemeal view by view is probably not a good idea, so this is more meant to show what it looks like so we can think about doing them all this way, or at least picture how it will be when we have client gen.Why
Short version: I want the imports to mirror the logical flow of data in the application. In this case that means that because we go from model to view, models should not "know about" views, which means the model file should not import view structs in order to define the conversions. Instead we define the conversions where the views are defined. This requires the views to be defined in nexus and not in common.
In my view, some architectural complication in omicron comes from the "view" structs living in omicron-common so they can be shared by the different apps. For example, the Instance view would be used in Nexus to serialize a JSON response, and would be used in another app to deserialize it from JSON. To me this is mostly a workaround for the fact that we're not yet using generated clients — once we have those, we do not need to directly share these structs. They can be defined only in the app that uses them as its responses, and the clients will use (presumably near-identical) generated versions.
In #188 we made it so the DB mostly doesn't know about the view structs, but the views were left in omicron common and the conversions from model to view were still defined in the db models file. This PR shows how it would look to move the views closer to their context of use.
Features of note
db::model
and definesFrom
, not the other way around.db::model
doesn't know about viewsdb::model
doesn't know about params either. conversion from params to model is done next to params definition. this is optional but I think I like it