I’m struggling with how I should build out my entities, services, and DAOs in an MVC web application when it comes to returning a list (array) of data. On one hand, returning an array of entity objects keeps the code lean and easy to maintain, however, there's a performance hit for building and returning complex entity objects when my view(s) may not need all that data at once.
For this example, let’s say I was creating a service to work with campgrounds (you know, those places with tents, lots of dirt, and mosquitoes).
Campground entities, in this project, are rather complex objects because they have many properties, nested value objects, and a few behaviors.
CampgroundService is going to have some pretty standard methods like
getById() method is mostly straightforward and will primarily be used when displaying detailed information on a single campground. Having
CampgroundService return a single
Campground entity here is the perfect solution.
Things get tricky with the
list() method. The
list() method accepts a filter object argument that contains criteria to filter by. For example, there may be times when I need to grab a list of campgrounds for an entire US state or a list of campgrounds based on a bounding box of geospatial coordinates (i.e. latitude and longitude).
The views that display the list (array) generally only need a small subset of the data contained in the full
Campground entity. For example, most of the time when listing I only need the
longitude. It feels like overkill to have to build out the full
Campground entity with all the nested objects every time I want to get a simple list of campgrounds and there is most certainly a measurable performance hit for doing so.
Hence my question: Is there a best-practice or design pattern handling listing data in an MVC app?
Here are some possible solutions I have been considering:
1. Caching to the Rescue? Caching would handle the speed problem on subsequent requests for data, however, storing potentially large arrays of entities in memory seems like a bit of a waste. Additionally, there may be times where I cannot really use caching because one of the views in the app sorts campgrounds by the geographic data type. I have MSSQL (via my DAO) doing the heavy lifting here for geo spacial sorting since it does it very quickly and the reference point for sorting could change rapidly (when the user moves the map around). Caching doesn't feel right for this use case.
2. Create a new, smaller “Summary” Campground entity
I could create a new entity object called
CampgroundSummary and a new service called
CampgroundSummaryService which would only hold the most basic fields for listing campgrounds. I expect this model would be much more lightweight and faster to assemble than the primary
Campground model. However, the downside is that I feel like I’m violating the DRY (don’t repeat yourself) principle by creating similar (albeit abbreviated) entity objects and services.
3. Return a Basic Array Instead I could forget about returning an array of entities altogether when listing data, and instead, just return an array of simple key/value objects. This feels very “noobish” to me, but the performance advantage could make it worthwhile. The downside of this approach is that I lose the advantages of using entities that enforce data integrity and behaviors (assuming I need them someday).
4. A Hybrid of Options 2 and 3
Simplifying the model into a
CampgroundSummary might be ideal in cases where I need to show a list of campgrounds by state, but only need a small subset of properties. I could even cache that data too to make it load lightning fast. However, for other cases where performance is the top priority, I could create a separate method in my
getSimpleList() which would return a simple array of key/value objects.
I would very much be interested in your insight on this subject.
Update: 7/12/2020 I had another possible solution:
5. Lazy Load Sub Objects
This strategy involves not assembling any complex sub-objects within the
Campground entity until they are needed. This means adding references to various service objects within the
Campground entity so it can retrieve that data when needed. This approach would certainly be more performant if the view does not have a need for nested object data. However, it depends on the view because I could see the view displaying a list of campgrounds along with something like
CampsiteTotal which would need to be calculated from sub-objects. It does feel a little like a code smell though adding service layer objects into my entities so I'm not sure how I feel about this solution.