Thanks for your help, Aaron. On Mon, Jul 24, 2017 at 11:01 PM, Aaron VonderHaar <gruen0aer...@gmail.com> wrote:
> Ah yes, a nice complicated system :) If you're still looking for more > abstract suggestions, here's how I would approach the problem: > Indeed the real system is much more complicated. This is a simulation intended to help understand part of what's going on in the real system. Eventually, we may actually incorporate it into the real system to help users visualize relationships in California property tax. It sounds like you're trying to construct intermediate data structures that > directly map to certain domain concepts, and then you later have to > transform that data into a structure that you ultimately need. I suspect > you may be able to clean it up a bit by focusing on what you need, and not > on what you think you ought to have. > That sounds like a good approach. As of my last commit, I have it representing AssessmentEvents and visualizing them. That involves some rather simple calculations, the results of which are stored directly on the AssessmentEvent object when it is created. I'm now trying to add Accounts and visualize those, nested under the AssessmentEvents. But the calculations to derive accounts are complex. At the boundaries of the system, you have structured input data (perhaps > being decoded from JSON?), possibly structured output data (perhaps being > sent out as JSON?), and the UI view. In my opinion, those are the most > important types in your system. Rather than trying to devise a data > structure that's somewhere in the middle of the input and output, I'd focus > on modeling the input and the output data structures in isolation, and then > try to figure out the shortest/most modular route for transforming the data > from one to the other. If you work in this way, I think you'll tend to end > up with more modular functions (which will also benefit you later, as you > mentioned anticipating having to handle realtime updates to the data). > For now, it's not doing any kind of JSON import/export. It's being initialized by sample data straight in the Main elm module. So perhaps it would be premature to model that? But I do intend down the road to add JSON import/export. There would be some script that extracts data from the real system and produces the minimal form of JSON expected to import into the visualization tool I'm developing. Another script could take the JSON exports and use that to simulate objects in the DB. Since the data displayed in my tool is a limited subset of read data, it will never be used to try to serialize or create real data. The JSON exports could be used to create test cases, though. My tool will be helpful in visualizing edge cases. > In what you're describing, the following things stuck out to me: > > > AssessmentEvents must in turn have been created by calling > createAssessmentEvent, which takes the independent fields of an > AssessmentEvent and creates the full record with the derived fields > > This sounds like it might be premature optimization. If you didn't > already try this, I'd suggest passing around the original raw fields > instead and exposing the functions that can compute the derived fields. > Furthermore, try having those functions take only the things that are > needed for that exact calculation, rather than taking the entire > AssessmentEvent record as an input parameter. Doing this will help expose > the actual dependencies in your data and avoid unnecessary coupling with > the "AssessmentEvent" concept. > Hmm, sure, I could do that. But it would change the interface. Right now, the view can simply deconstruct the AssessmentEvent to get any of the originally supplied fields that it needs to render, such as eventDate, or any of the calculated fields it needs, such as effectiveDate. So I would have an inconsistent interface if it needed to call a function to derive one but not the other. But I suppose you are suggesting that I hide the representation entirely, so that a function has to be called in either case. That would work. (I'm already doing that with some of the newer types I've added, such as DateInterval.) > when outside code needed to get the delta value, it couldn't just have an > AssessmentEvent. It would have to have an AssessmentStore (or Parcel) and > an EventID > > I would work backwards here and start with what data structure makes sense > in the view, and then write the code to generate that from the raw data, > and then see if there are logical groupings that make sense to refactor out > as data types/modules (as opposed to starting with the domain concepts you > think you are supposed to have and trying to write your view to work with > those). > OK. The existing AssessmentEvent structure works for the view, of course, since I already have that working. But I think you mean here not just AssessmentEvents... but what is the full picture that the view needs of a Parcel and all its constituents. Are you suggesting that I have one set of datatypes for supplying input to construct the model, another set of datatypes for the model, and a third set of datatypes for the view? Or are you suggesting that the latter two (model and view) use the same data structure, but that it's influenced only by what needs to be in the view? Does your view just want a list of things to iterate through and display? > For the moment, yes. It's already iterating through AssessmentEvents, and now it will need to iterate through each of their Accounts as well. The next step after that would be to model and display Bills under the accounts. > If so, it sounds like you want to give it a list of records that have all > the necessary data assembled so you can just iterate through it. Or do you > have some kind of master-detail view where one view is showing the details > of a thing that is chosen in another view? > That will come in a later step, yes. The ability to hover over any of these entities and get a detail view in another div. And perhaps also cause related entities to be highlighted in some way. I'm not sure whether I should design for that yet, or take it one step at a time. > In that case you might want to have the selector view produce an Id > that's stored in the model and used to later request a specific item to be > calculated for the detail view. Or maybe different parts of the view show > different pieces of information, each of which is hard to compute? > The detail view will be showing some pretty simple information. Just details that are already on the entity, or are simple calculations of the sort that are used in displaying the entity itself. It's possible I may not even need IDs to do this. Not sure yet. (By "entity" I mean either the Parcel (top-level, or AssessmentEvent, or Account, or Bill.) In either case, I'd try writing your view the way you want it, then write > the function to transform the data how you need it, and then decide whether > that function (or parts of it) make the most sense in the view module or in > one of the data structure modules. > I think I'm pretty clear on which calculations belong in the view and which ones don't. The view will just be calculating things to do with layout, and which object is selected. It would not make any sense for the view to compute the Accounts, for example. Overall, my suspicion is that you might be trying to specific domain > concepts that you are expecting to have but are possibly unnecessary for > what you need to do. So it might be useful to try to cleanly model the > input data and the data you want to display, then write the functions to > map between then, and only then figure out how those functions map to your > business domain as you refactor. Based on what you described, it sounds > like Parcel, AssessmentEvent, and Property are all getting quite > interconnected. > Property is not one of the types - it's the module that contains the domain model. For now I have the input data quite simply modeled by some anonymous record types. However I think I am complicating the steps a bit. Rather than putting all the input data into one structure and passing it in, I have createAssessmentEvent that constructs an AssessmentEvent, and createParcel that takes a list of those, plus other top-level data. So what I hear you suggesting is to make that cleaner by just modeling the input data with its own set of structures. It seems my approach is not so different from how many of the Elm libraries are written, except that they would explicitly hide the data constructors. But the Elm Design Guidelines explicitly say to hide the constructors <http://package.elm-lang.org/help/design-guidelines#keep-tags-and-record-constructors-secret> . > If you instead try to focus on transforming from input to output as > directly as possible, I think you'll end up with a system that's easier to > modify or reconfigure later. (The downside is that it may seem unnatural > to people who are used to thinking in terms of the standard business domain > concepts.) > Well, essentially I'm doing that already. With the exception that I have that intermediate step of calling createParcel/createAssessmentEvent. Actually createParcel is doing all these computations that builds the model that the view wants to see. I'll note that the suggestions here match the way I personally like to > approach problems, which is to focus on iteratively discovering the > interfaces. If that style doesn't match the way your team works, you > should disregard :) > Well, this is a solo project, done in my 10% time. So I'm not coordinating work with my team. Everything else we do is in Perl. With JS on the front end. I'm a Haskell coder and thought this would be a good application for Elm. Also, would be happy to look at some type annotations if you want to talk > more concretely. > That would be great, very kind. I'd prefer to take it off the list if we're to get into more detail. Thanks, Lyle P.S. I originally sent this mail on 25 July, but sent it from the wrong address so I guess it didn't get posted. It didn't bounce either. -- You received this message because you are subscribed to the Google Groups "Elm Discuss" group. To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.