Object-Oriented Site Design
Website ·I’ve done some work (part 1, part 2) to set up a simple test rails app, but it’s time to expand its functionality a bit to actually make it useful. We’ll do that by thinking through user-centered design and domain driven design (DDD).
Goals of the app
Pun intended! The goal is to provide a single location for aggregated statistics from various sources. I want a user to be able to look at a league table and see quickly who is outperforming their xG, and how a club’s ELO rating has fluctuated over time. Seeing all upcoming games across leagues would be useful, as would a clear visual of the financial inequities in given leagues and fixtures.
The site should be refreshed daily with a clock job, and there should be a button that refreshes all data manually. A more granular level doesn’t make sense since all the data is interrelated.
DDD
Right now, tables and table rows (both from FBRef) are the building blocks of the site. Instead, clubs should be the building blocks (source agnostic). However, clubs do not belong to leagues consistently, so we will want to make sure we don’t hard-code that link, but rather do a lookup based on country.
We will also need to keep in mind the league/country divide as an edge case, since Swansea City (Wales) play in the English Championship.
Breaking down steps to take based on design plan
At some point we will build an exporter to export club-specific info in addition to table info. So for each exporter, we’ll need both source and type available. So table_exporter
can be one, and club_exporter
can be another, nested in an fbref
folder, with appropriate configurations nested underneath. For now, a data refresh from the homepage will export all table data, and from each table page will just export that table’s data (this is already mostly implemented).
The main new piece we need for now is to build up club data from the table exports. Clubs will need configuration based on site (for FBRef, for example, name, country_abbr, and url). So the club’s model should consist of: name, location, country, config (config is nested site-by-site data when exporting just a club, or when exporting tables where we expect to find club data).
Then how do we populate the club’s data from the exported table data? We’d be adding far too much logic to the controller, which should be kept as minimal as possible. So let’s implement a service object, to continue practicing that abstraction structure.
Some challenges
How to handle looking up the nested fbref_table_name
key-value pair in the config
db column. I don’t love it, but the scale of things at the moment means we should be able to handle this solution not being the most efficient.
Interesting syntax thing on conditional link rendering.
On the other hand, the table/club split is simplifying things so far. The tables controller #refresh_all
method is refreshingly straightforward:
def refresh_all
Table.all.each do |table|
table_refresh_service.refresh_table(table[:id])
end
redirect_to root_path
end
Progress update
Everything pictured is working (along with table sorting)!