I don’t think you need middleware (you could use it, but it’s not necessary).
Link all the records from each of your three tables to a single record in a new table. (I posted a quick how-to last night in response to another request.) At that point, you can access any field from all three tables from your fourth table. Depending on what you do and do not wish to see, you might have to add some formula-driven filtration fields to your original tables so you can roll up subsets of data.
If you want to display or print the rolled up values, you can do so using
ARRAYJOIN(values,'\n'). This will return every entry in the specified field in a single column with a carriage return and line break after each item.
When it comes time to write the code to handle end-of-month calculations, I urge you to look into using aggregation formulas. An amazingly powerful un[der]documented capability of Airtable is that one can craft full-blown formulas inside rollup field configurations rather than being limited to single functions. You lose the contextual help provided by the configuration editor, as it’s not yet been informed of this enhanced functionality; you also can access only a single field from the remote table, which must be referred to using the keyword
values. (There are numerous examples of aggregation formulas in my demo tables and related posts. For example, this reply includes a stunningly ugly specimen containing 98 lines of code.)
Despite any difficulties introduced or exacerbated by the use of aggregation formulas, the potential gains in performance and elegance greatly outweigh any negatives. (Did I really write that?) Take Two: Sure, it’s a little harder, but it’s worth it, in terms of performance, legibility, and maintainability.
This is especially the case when rolling up large values. Using an aggregation function, you would ordinarily rollup a large value to the joining table — after which you would then perform a lookup or rollup to duplicate said value onto every record in the original tables. Finally, once that was done, you could create a formula field to make use of the aggregated value in a calculation.
With aggregation formulas, though, you still do the initial rollup of the large value to the joining table. After that, though, you roll up that value from the original tables — but you do so with a formula embedded in the configuration of the rollup field. This allows you to access the large value by reference, using that single instance in your calculation, rather than having to create a copy of the entire thing for each formula field. In one of my demo bases, changing from aggregation functions to formulas trimmed the equivalent of three copies of War and Peace from the size of the base’s footprint. That was with 1,000 records in the base; because of the exponential growth rate of the value in question, at 10,000 records a single copy of that field would be as long as a novel — making a copy of that base with a lookup field duplicating that value onto every record about the same size as the average U.S. public secondary school library.
Admittedly, a lot (all?) of this is non-intuitive and hard to decipher, even on days when my writing skills are firing on all cylinders, as they are not so doing, today. If you would like to share a copy of your base (either by posting or PM; prefer a read-only link to the entire base with copying enabled), I’ll try to rough in how this might work.