Should IResource be "dumb" or smart?See original GitHub issue
With the imminent merging of the interface branch, we currently have the following pattern which has become much clearer as part of that refactor:
IResource. Represents an ARM resource in F#. Examples include:
Network PublicIPAddress VirtualNetwork NetworkInterface Web ServerFarm Sites
An IResource is typically a record and has two members - a Resource Name (used to detect duplicate resources) and a
unit -> obj function which knows how to create a “JSON ready” object containing only primitives ready for serialization.
ResourceBuilder). Given a location, generate a list of IResource objects. ResourceBuilders are typically Records (typically with the postfix
Config) and are wrapped by computation expressions builders (ignore the new ResourceBuilder approach for this discussion).
The pattern looks something like this:
ResourceBuilders are typically strongly typed, using discriminated unions and the like in order to construct types which, more or less, make illegal states unrepresentable. I say “more or less”, because there are some compromises that have made in order to keep the API as simple as possible from a consumption point of view (so that a minimal amount of types are needed when consuming any builders etc.).
The question is what constraints should the IResource make (before JSON serialization occurs)?
Option 1: Make them simple types with primitives only
This makes the mapping to the JSON-ready object very simple as the values are already primitives. The mapping instead happens in the IResourceBuilder at the point of generating the IResources. The “cost” of this is that if you want to use the same IResource type in multiple builders - for example, an IPAddress or a ServerFarm - you have to do the same mapping from a “smart” IResourceBuilder to a “dumb” IResource.
This is the current approach we now have in the latest version of master.
Option 2: Make them smarter types.
We make IResources smarter and put richer types on them. The cost of “dumbing down” happens right at the end, when we move to the JSON-ready object, and only then. The benefit of this approach is that the mapping to JSON is guaranteed to happen in a typesafe manner, and the mapping from IResourceBuilders to IResources will be relatively simple. The concern is that, assuming that we share typed because the IResource and IResourceBuilders, then the types that these records have should still live at a level that the consumer can easily get to - typically, just by opening
Farmer.Resources (or even better, just
Farmer). If these types instead live next to the IResource, users will need to fumble around knowing which namespaces to open, and the whole exercise of “ease of use” (which is a key driver behind Farmer) will be lost.
Appreciate this might seem like bikeshedding but nonetheless, if you’re interested in this, I’d welcome thoughts and feedback.
A good example is to look at
ExpressRouteCircuit in the current master, which has been completely “dumbed down” as part of my merge into the new API cc: @ninjarobot . Previously it had stronger types.
- Created 3 years ago
- Comments:11 (10 by maintainers)
Top GitHub Comments
You forgot the sarcasm font 😛
Type-safety, building blocks, and seams
I do not think that giving users the option of using an
IResource a problem, as long as we do provide higher-level constructs that do offer that ie. the
Builders. And maybe this is where we are diverging on view…
Currently, I see
IResource as a raw persistence/transport implementation. It is a seam.
Imagine for example we needed to support different ARM template versions and a builder would pick the IResource to output. Or Azure releases YARML 😛
Another desire for seems may come from a particularly tricky mapping. You might want to switch from a more testing Builder -> Json and split them Builder -> IResource; IResouce -> Json.
Viewing it this way makes
IResource an implementation detail but one that we surface as a fairly first-class citizen purely because this is a library, not a framework (?). Maybe we are diverging in opinion here though, as I know you wanted to make Farmer opinionated.
What I am suggesting is let’s have the low-level building blocks, the clean type-safe wrappers and syntactic niceness around the building blocks of Azure, and then tighten the opinions in the “topology” layer.
The naming is a little confusing I think because we have 2 layers that represent the same thing. Maybe this is a tell and we need to simplify but for the sake of exploring the possible layers, let us push on.
Some suggestions for sharpening up the naming:
IArmResourcebrings it back to it being specifically for outputting the format for the resource in the ARM template. Other words that might fit to make this specific ResourceTemplate, ResourceSchema, …?
- Config and Builder: Although I don’t hate Config, Builder or CE triggers me a bit as a way to refer to them collectively. Maybe collectively these are just a Resource but I think it would be less confusing if naming was easy to differentiate (not IResource and ResourceBuilder). I went on to the Azure site and see what they call it… so from an Arm concept, they call them resources of course. From an entry point though they call them Service or Product. I know Service is soooo overloaded… Just putting out some ideas here.
- Typology: I do not think this is the right name for this. I am not sure we are clear on what we are aiming for here, and it can likely vary greatly. But I was thinking along the lines of where you were going with a Safe app @isaacabraham . I don’t know that anything that specific is needed. Configuring a system made up of multiple containers. What about Recipes? Examples – Web app with database – Static site – Batch processing
Making it simple to have the building blocks so people could create their own Recipes would be awesome. I mean that is already possible really but even in naming, we could show that this is the intent. I have worked in big dev departments were rolling out like this in a parameterized way would have been awesome.