Skip to content
This repository has been archived by the owner on Oct 19, 2018. It is now read-only.

xx old 2 Overview

Barrie Hadfield edited this page Jun 24, 2017 · 1 revision

These page contains speculative potential direction for Hyperloop

Hyperloop is an Isomorphic Web Application Framework

Background

This proposal outlines how Hyperloop can become a ‘better’ architecture for Isomorphic Web ApplicationS (IWA) that borrows from the best of the frameworks available today yet differentiated by overcoming some of the negative affects of the status quo plaguing modern web development - namely the huge productivity drain and 'cost/complexity of change' of building and maintaining a multi layered responsive client architecture as well as a highly structured API based backend architecture including client-server data synchronisation and authorization.

Complex systems like this are necessary due to consumer expectations put on a modern user interface. They are expected to be highly responsive and intuitive and the single-page-model proposed by Rails does not suffice for all but the simplest of data centric applications. In today's world we have to think about the UI as being an application in its own right, most likely written in JavaScript. We end up with a multi-layered monster of UI, API, controllers, push notifications, models, microservices, Rails architecture - all written in multiple languages and technologies, all needing testing and teams of people. Innovation is sacrificed by the enormous weight of administration and developer productivity grinds to an all time low under an ever increasing technical dept.

It does not have to be this way!

The answer however is not as simple as redefining the View layer or just moving to one Isomorphic language. We need to think about the whole web application stack and propose an architecture and set of technologies which work coherently together to maxamise developer productivity.

What would an ideal web application architecture look like – what would be the goals?

  • Highly responsive UI (React based UI)
  • Distributed processing (uses client as appropriate)
  • API optional (used if needed for extensibility)
  • Client data synchronization as a part of the infrastructure (not application logic)
  • One set of business objects - models (available to client or server code)
  • Business logic mutation layer (easy to change and easy to test, runs on client or server)
  • One authorization system (for access and data distribution)
  • Database agnostic (through models)
  • One language Ruby (which will compile where needed – JS, Webasm, C via Crystal)

[The mutation layer is an important element. The idea of keeping all your business logic in one place is extremely strong and should, therefore, be a fundamental part of defining a new web architecture. Using the models is a great way to render data; the models describe the business objects well but, by not specifying where and how data is modified, can lead to spaghetti code. A key process, then, is defining an architectural layer where all data mutations exist (aligned with authorization). As a result, the argument about the need for an API is secondary - an API is only a necessity if, for example, a 3rd party development is given access. The API layer would then, of course, access the models for reading and the mutations for writing data. The mutation code could be client or server (depending on the application). ]

Proposal

An Isomorphic Web Application (IWA) Framework as far as is practical shields the developer from worry about where code runs, and lets the developer focus on what the code should do. In addition like any framework is supports a standardized structure so that code can easily be partioned, maintained and reused. Hyperloop divides code into the following types:

  • Models for accessing data and encapsulating business objects
  • Operations for mutating data and encapsulating business logic
  • Components for interfacing with the user and encapsulating user interactions
  • Decorators for providing reusable methods that can be used by the components
  • Stores to provide a common access point for components to update and read state
  • Routers for matching routes to components

[You will notice that there are no "controllers", ... blah blah ... going to bed but operations + decorators take care of the function of controllers.]

Operations

# Define a command that signs up a user.
class UserSignup < Hyperloop::Operation 
   # note no base, as I doubt you would ever subclass off of something else
   # thus Operations can only be defined as subclasses of HyperLoop::Operation

   # restrict access to the operation to logged in users.
   allow_remote_access { acting_user }

   # HyperLoop::Mutations use syntax consistent with React::Component::Base
   # here email and name are required, newsletter_subscribe is optional
    param :email, type: String, matches: EMAIL_REGEX # matches would be a cool add to React::Components!
    param :name, type: String
    param newsletter_subscribe: false, type: Boolean

   # Instead of the render method we have the execute method
   # execute is called only if the inputs validate and the acting_user is non-nil (in this case)  
   # It does your business action. 
   def execute
    user = User.create!(params)
    NewsletterSubscriptions.create(email: params.email, user_id: user.id) if params.newsletter_subscribe
    # have to figure out how the below works... I am figuring that you have server side mutations as well
    UserMailer.async(:deliver_welcome, user.id)
    user
  end
end

Authorization

Operations executing on the client side are subject whatever Authorization policies are defined. For example if an Operation attempts to read or update a Model, the Model's access policies will be enforced. This is sufficient as the only access that the client has is through the models, so protecting the models will protect the server.

On the other hand an Operation executing on the Server has access to the entire server environment. For example consider an Operation that uses a payment processor API - client access to server side operations must therefore be regulated independently of the model policies.

To regulate client access to server regulations, the policy mechanism defines the allow_remote_access regulation which can be defined either in the Operation itself, or in a Policy:

# the regulation can be defined directly in the operation class:
class UserSignup < HyperLoop::Operation
  allow_operation { acting_user } # must have a logged in user to do this operation
  # allow_operation { acting_user.admin? } # would mean only admins could do the operation
end

# alternatively the regulations can be grouped in a general policy class (i.e. ApplicationPolicy or OperationPolicy)
class OperationPolicy
  allow_operation(UserSignup, SomeOtherOperation) { acting_user }
end

# or even better
class OperationPolicy
  UserSignup.allow_operation { acting_user }
end
Clone this wiki locally