I am trying to model a domain of metrics for use in a BI application.  I recently read the Designing with Types series, and felt that it could be useful, so I wanted to give it a try.  What I'm not sure is if I'm trying too hard....The metrics are application-defined, meaning they are finite and won't change during runtime.  A dozen metrics would probably be a lot.New metrics will be introduced as the application growsA user will be selecting which metrics they want to use, and the application has to take that and go do stuff, like building a query to the backend store (MongoDB, in this case).There may be application logic associated with metrics that I would like enforced through pattern matching so that when I introduce a new metric, I want the compiler to tell me that I need to do some work.Here's what I have:type Metrics =  | Revenue  | Volume  | PriceAsp  with    member m.FormatGroupings =      match m with        | Revenue -> [(revenue, $sum: { $Amount } )]        | Volume  -> [(volume, $sum: { $Quantity } )]        | PriceAsp -> List.append Revenue.FormatGroupings Volume.FormatGroupings    member m.FormatProjections =      match m with        | Revenue -> [(revenue, $revenue)]        | Volume -> [(volume, $volume)]        | PriceAsp -> [(priceAsp,  $divide: [$revenue, $volume]  )]let buildQuery groupBy (metrics:Metrics list) =  let concatenate f =    let x = metrics            |> List.collect f            |> List.map (fun m -> sprintf {%s: %s} (fst m) (snd m) )    System.String.Join(,, x)  let groupings = concatenate (fun m -> m.FormatGroupings)  let projections = concatenate (fun m -> m.FormatProjections)  sprintf {$group: {_id: {%s: $%s}}, %s}, $project: {%s}} groupBy groupBy groupings projectionsWith examples of usage:buildQuery foo [Revenue; Volume]  //SimplebuildQuery foo [PriceAsp]         //CompoundNote that PriceAsp is made up of both Revenue and Volume.Please provide any general comments.  Some specific questions I have are:Have the business rules for the metric calculations be embedded within the typesShow that some metrics are built off of other metrics (for example, PriceAsp), and enforce the explicit relationship.  Above, we see that PriceAsp is made up of Revenue and Volume because it's in that list.  But it would be better to make it enforced explicitly through typing.There are formatting rules around an instance of a specific metric.  For example, when Revenue is shown to a user, it should be rounded to the nearest whole dollar, with thousand separators and a currency symbol (ex:  $1,234,567).Is there an elegant way (using List.fold, perhaps) for doing what String.Join does?  String.Join is just so convenient...A base metric would look like this (no $project):{  $group: {    _id: {      foo: $Foo    },    revenue: { $sum: $Amount }  }}A compound metric may look like this:{  $group: {    _id: {      foo: $Foo    },    revenue: { $sum: $Amount },    volume: { $sum: $Quantity }  },  $project: {    priceAsp: { $divide: [$revenue, $volume] }  }}
Using Types to Designing Domain
f#;mongodb