Exploring "GOOS" in context of web applications development with Rails and Rich UI

149 views
Skip to first unread message

Yevhen Viktorov

unread,
Jul 30, 2018, 2:20:25 AM7/30/18
to Growing Object-Oriented Software
I keen to build sample app, but am I on the right track or missing the point?

Given a part of ordering system where, among other features, user can create
an order and make a payment. And given basic rails setup with extra gems for
enabling rich UI. I'd like to explore how would tests looks like.

Quick, high level, look into architecture reveals the following key layers:

* Rich UI (REST client?)
* CRUD (REST server?)
* Yet another domain (whatever...)

While not as RESTful as defined by Roy T. Fielding, Rails embraces resource
as a central model and makes it universal communication entity which crosses
all layers. It forces us to define resources sooner and maintaining low coupling
to its structure in every layer including tests.

And as long as it makes sense, I imagine the tests would looks like this:

### End-To-End

Covers observable application behavior and interaction with 3rd party services.

```ruby
describe "pay full amount" do
  let(:order_page)   { application.open order_record }
  let(:order_record) { an_order.with(a_line.with_price(100)).create }

  before do
    order_page.make_payment amount: 100
  end

  it 'marks order as paid and sends to the manufacturing queue' do
    expect(order_page).to have_status_paid
    expect(manufacturing_queue).to have_received order_record
  end
end
```

* `order_record` - persisted active record model
* `order_page` - manipulates and assert UI (capybara page wrapper)
* `manufacturing_queue` - external service

### Rich UI

This layer enables interaction with the system in a convenient for the humans
way. Basically it consists of a set of UI components, translates user
activities into requests to the next layer and interprets responses.

The test for this layer would share matchers with end-to-end scenarios and might look like this:

```ruby
# creating order
describe "when create button clicked" do
  let(:new_order_record) { an_order.build }

  before do
    order_page.create_order new_order_record
  end

  it 'makes request' do
    expect(application).to have_received_request :create, new_order_record
  end
end

# and for making payments
describe "when pay button clicked" do
  let(:order_record) { an_order.create }
  let(:new_payment_record) { a_payment.build }

  before do
    order_page.open order_record
    order_page.make_payment new_payment_record
  end

  it 'makes request' do
    expect(application).to have_received_request :create, [order_record, new_payment_record]
  end
end
```

* `new_order_record` - non persisted active record model
* `new_payment_record` - non persisted active record model
* `order_record` - persisted active record model
* `order_page` - manipulates and assert UI (capybara page wrapper)
* `application` - rails application runner (or wrapper)
* `have_received_request` - custom matcher which decodes request details from
    the type and active record objects (similar to respond_with from Responders gem)

### CRUD

Provides resource manipulation interface.

Basically level two in Richardson's maturity model and Rails fully covers
this layer, i.e. form input to output, from handling request/response,
to persisting resources in database of choice.

The test for this layer might look like this:

```ruby
describe "POST /order/{id}/payment", type: :request do
  it 'creates resource' do
    make_request :create, [order, new_payment]

    expect(response).to have_http_status(:created)
    expect(response_json).to include(
      id: a_kind_of(Integer)
    )
  end

  it 'makes user request' do
    expect(user_request_listener).to receive(:process_payment).with(
      gateway_id: user.gateway_id,
      amount: order.amount
    )

    make_request :create, [order, new_payment]
  end
end
```

* `new_*` - not persisted active record model
* `order` - persisted active record model
* `make_request` - helper method, makes HTTP request based on the type and
    resources specified (similar to respond_with from Responders gem)
* `user_request_listeners` - mock object

### Yet another domain

This is where "interesting" things happens as a side effect of resource manipulation.
For example: payment resource creation would trigger payment processing behavior
or "return order" creation would trigger behavior responsible for adjusting
stock levels, cost prices, etc.

Test for this layer usually dive into details of business domain, covering
rules, validations, exceptions, etc.

Does it make sense at all?
Please let me know if any questions.
I'd appreciate any opinions and criticism.

Thank you in advance!

Yevhen Viktorov

unread,
Aug 8, 2018, 7:44:42 PM8/8/18
to Growing Object-Oriented Software

Sample Web App:

"Introducing payments to Order Management Web App on Rails, with Backbone.js as frontend, and REST in between."

Source on GitHub with more details in PR description:
https://github.com/yevgenko/rich-and-restful/pull/1

I'd appreciate feedback and questions.

Thank you in advance!
Reply all
Reply to author
Forward
0 new messages