How we build web pages: rails views vs angular templates


#1

If we want to get closer to having an API based application, we need to stop building screens using rails views accessing the rails models. We need to move into the use of the API for every part of the app. Either by having frontend code (like angular) accessing the API (or by having the rails views depending only on the API but not the OFN services or data models). Spree screens are rails views that depend on the underlying model BUT this logic of building on top of the API can be used for all the spree screens that we replace/rebuild to adapt to our needs.

The main argument for this approach is that by doing this we are developing, extending, testing and bullet proofing the OFN API. If we don’t depend on the API to build OFN’s screens, we will not have a working, complete or resilient API in the long-run.

Above I describe option 1 that I call “API first approach”, all views are built on top of the API.
There are 2 other options at least:

  • options 2 - we keep building OFN’s screens with a mix of rails views and angular components (using the mix of posting data to the API and using AMS to feed data into angular through the DOM)
  • options 3 - we decide we do API first approach ONLY for the FrontOffice. We can keep the BackOffice as it is with our custom views of Spree data, with existing defaced Spree views and also new views copied from Spree BUT we make all FrontOffice views in Angular only and make it only depend on the API.

I think option 3 would be a good balanced approach because this would enable us to keep the complex rails views on the BackOffice/Spree side of things and, at the same time, test the detached approach where the FrontOffice would run on top of the API only. This would also support the development of the API for OTHER front-offices and shops.

In practice, what option 3 would mean is that we would detach the Darkswarm application from the OFN application.

I am raising this question because in my opinion the current approach “option 2” is not sustainable, because the rails code and the angular code is tightly coupled/intermingled and that makes for slower/difficult/error-prone changes that do not support the evolution of the API.

Please let me know if I explained options clearly and what do you think about this decision!

References:

  • this is related to the evolution of the OFN API
  • this thread is to serve as detailed discussion on the first topic here. After this discussion, we can then take decisions into the wiki page.

Enabling OFN to integrate with external systems (POS, standalone websites, Odoo ERP modules, other plateforms with DFC sandard, etc.)
#2

So if I understand correctly, by

we decide we do API first approach ONLY for the FrontOffice

You mean we stop relying on data being injected into the DOM and make API calls once Angular is loaded?

Also, in option 3 by

with existing defaced Spree views and also new views copied from Spree

you mean that we keep everything as it is but only new developments of the backoffice will require the view to be fully implemented by OFN? Basically what is explained in https://github.com/openfoodfoundation/openfoodnetwork/wiki/Code,-the-way-we-do-things#re-building-spree-views ?

For me, a first goal would be to get rid of Deface and at least understand and document all the different ways we have to access data from the client-side. I guess that might be documented somewhere already. After that, we can design a plan.


#3

@oeoeaio @maikel @Matt-Yorkley @enricostn @Hugs @andy maybe you have some ideas to share on the topic as well?


#4

@Kirsten you might have ideas on this as well…


#5

Yes yes yes yes. I didn’t build the frontend, but I never understood the reasoning behind injecting data into the DOM instead of building, maintaining and using an API. If we can decouple the frontend views from rails, then we will be a big step closer to having a product that people can install on their own servers and can customise any way they like. That will be cool.

Option 3 seems good to me. I will not be easy, but I support it as a general strategy.

I think the easiest way to get rid of deface would be to accept that we have diverged enough from Spree’s admin interfaces to justify ignoring them completely and copying everything that we still depend on into the OFN as if it were our own code. At least then we would know where to look for things.


#6

You put that perfectly! ok. Where do we write this down to guide us for upcoming PRs? that goes beyond the Spree upgrade. Shall we start a new section of the wiki with this architecture/design decisions?


#7

awesome, thanks a lot for your feedback guys!

@oeoeaio I read on the wiki that one reason for injecting data instead of using the API is that “to inject JSON data into pages onload … is faster than an Ajax request.”
Anyway, we agree :smiley:, we need to move away from this and keep growing the API.

re Spree screens, I follow you guys on wanting to make spree views our views. I think that is the right direction. I just thought of mentioning the problem with that: next time you upgrade spree (to spree 4), you will have to rewrite/adapt all the copied screens…

This page is for that (you can rename the page :smile:) https://github.com/openfoodfoundation/openfoodnetwork/wiki/Code,-the-way-we-do-things


#8

It’s an antipattern (exacerbated by idiomatic rails) that we typically only create one view per controller. The whole point of MVC is to create an api, and render different views in different contexts.
We should really not be defining anything domain specific in Rails (again, bad idiomatic Rails); we should be delegating out of Rails immediately, into our own domain logic in lib
Rails tries to do too much, and as a result, does lots of things badly. The vast majority of code in the legacy Rails codebases I’ve worked on is discarding Rails defaults and overriding them with something sensible. (this is one of the reasons that I prefer Sinatra, as it just handles the HTTP routing – one job, done well, with minimal fuss)

I don’t have enough context to give specific guidance, but general guidance is that we should aim for high-cohesion. Things that belong semantically together should have the minimum distance between them. This is one of the reasons that I like Polymer/Web Components… the presentation and behaviour are encapsulated into discrete units.


#9

Interesting.
Currently we have most logic in controllers, models (oh yes!) and helpers. It’s how these MVC frameworks (Rails is just one of many) make people code…
I think it’s a natural process when you become more experienced and when you work with larger systems to dislike frameworks with strong opinions like Rails and prefer libraries (like sinatra) that help you with specific problems.

Anyway, one of the simple ways I see we can implement what you are describing is to start talking, defining and growing a service layer in the application under app/services. This would be where we start putting our logic in a way that is separated from the MVC game (and consequently from the Spree game).
The rule would actually be quite simple: if you have any logic in the view or controller, move it to a service under app/services, if you have any logic in the model (that doesn’t depend directly on ActiveRecords) put it in a service under app/services.


#10

what you describe with app/services is also a well-established first practices in the Rails community and there’s plenty of literature about the topic. So it feels like a good first step, that we already started timidly adopting (w already have 13 files in app/services/). I think we’re only missing the explicit agreement and enforcement.

I also believe 7 Patterns to Refactor Fat ActiveRecord Models is also a classic reference on the topic that is worth sharing.


#11

YES!
Awesome article!


#12

I agree with copying Spree views into our code base to get rid of defacements. We have so many customisations that it’s less work to figure out which impact a Spree impact has on our views than to figure out how an update changes our views and which implications that has on our customisations. And going in that direction, the better we separate us from Spree, the fewer impacts there will be with an update.

Injecting data into a page doesn’t seem to be a bad thing for me. It can be more efficient for the user and it was done right, there would be almost no additional code for that. Unfortunately, our use of AMS with Angular requires a few additional steps to initialise our data models in the browser. I’m happy to move away from that towards more use of APIs, especially if that means we can use the Browser’s cache more efficiently. For example, we pages can be cached and the data gets updated via JSON calls when needed. The server needs to know when to send “not modified” to make this really efficient.

I still prefer to render most of the views on the server though. I find heavy uses of Javascript rendering really slow, even with a fast computer. So instead of being dogmatic about Rails views vs Javascript rendering, I still think we should weigh it up on a case by case basis.


#13

thanks for your feedback @maikel

We are aligned on the data injection vs api. Yes, api cache is a interesting topic.

I still prefer to render most of the views on the server though.

Any dev team is an heterogenous group of people: some will prefer the server, some will prefer the client. It’s a fact of life.

I find heavy uses of Javascript rendering really slow, even with a fast computer.

Done properly (for example, with proper asset and API caching) a JS website can be much faster than having to wait for the server. And cheaper on the server bills also with a lot less CPU usage, i.e., more scalable.

So instead of being dogmatic about Rails views vs Javascript rendering, I still think we should weigh it up on a case by case basis.

This is the main point of the post, I don’t think this is being dogmatic, this is about having or not having a strategy. If you don’t define a strategy, you will never get to where you want to be.
I believe that if you don’t decide and keep on option 2 (as we are with a mix of rails and angular) instead of moving to option 3 (FrontOffice in API/JS only) you will never have an API that easily supports the creation of external front-offices.


#14

I find heavy uses of Javascript rendering really slow, even with a fast computer.

Done properly (for example, with proper asset and API caching) a JS website can be much faster than having to wait for the server.

Everything is much faster the status quo when done properly. :wink:

My concern is not the slowness of transferring the data to the client. I was talking about slow rendering on the client of dynamically changed HTML. I definitely notice that.

In the big picture, I’m not sure what is more efficient. On one hand we have an expensive server doing a lot of work for all users. It’s on special hardware in a big data centre that has lot more overhead than your home computer, but is also very efficient. On the other hand you have thousands of client devices. The users have to buy phones that are fast enough and charge their batteries more often, because they are using websites that render on the client. I don’t know which of the two is actually more efficient.

I think we all agree that we want a better API. And since our resources are quite limited, we can only afford one API which our frontend code shares with potential third party sites. I completely support our move towards improving and using our API. But I don’t see why we need Angular templates to use our API. It’s the other way round. We have to use an API if we want to use Angular templates only (and not inject data via AMS). Looking at it from that angle, as you do in your post, I can understand your point that using Angular templates would help us creating a complete API.

But I’m not convinced that using Angular templates everywhere is what we need at the moment. Of course, I’m just one voice. While I’ve seen a lot of really good contributions to this discussion, I actually haven’t seen any clear vote for or against it (except from you). What does everybody think? I haven’t been in Barcelona. I don’t know what you discussed there.


#15

On the performance topic, I am also talking about page load time after all rendering is done. On my previous experience, we migrated an MVC asp.net website to a react version. Initially the page load times were worse but after some performance improvement efforts the website was getting faster… to you point, with time, you can make any solution fast.

I think we all agree that we want a better API.

I think this is what really matters for OFN :slight_smile:

Apart from Rob’s comment above: “Yes yes yes yes.” to “If we want to get closer to having an API based application, we need to stop building screens using rails views accessing the rails models”, in Barcelona, Hugo, Matt and Pau opinions tended more to the rails views side. But I think we need to see what exactly is the rails views side (see next paragraph).

At this point I think it is useful to go through the details of what I mean by the options with angular or rails views.
I don’t think that with option 3 we should remove all the rails views all together and move everything to Angular. With option 3 what I am proposing is that we agree that to improve our API we build our screens using data coming from API endpoints and not AMS injected in the DOM or rails models directly. With option 3, we would agree that on the front-office we would follow this rule.

To give a specific code example: in the front-office we would not use the InjectionHelper and we would not use rails models objects,
like for example the call to admin_inject_monthly_bill_description and the use of enterprises object in - /app/views/enterprises/_admin_index.html
We would rather use the API to collect the data used in the page like for /shop/products here: /app/shop/products/_form.html
I hope these specific code examples help the conversation!

It would be great to have other opinions on this thread!


#16

Yes, I agree to using more API and less injection helper.