mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-09-30 22:41:23 +00:00
224 lines
11 KiB
Markdown
224 lines
11 KiB
Markdown
+++
|
||
title = "Two Approaches To Decoupling"
|
||
date = 2022-05-01
|
||
updated = 2022-05-01
|
||
[taxonomies]
|
||
author = ["Carson Gross"]
|
||
tag = ["posts"]
|
||
+++
|
||
|
||
> The central feature that distinguishes the REST architectural style from other network-based styles is its emphasis on
|
||
> a uniform interface between components. By applying the software engineering principle of generality to the component
|
||
> interface, the overall system architecture is simplified and the visibility of interactions is improved.
|
||
> Implementations are decoupled from the services they provide, which encourages independent evolvability.
|
||
|
||
_-Roy Fielding, <https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5>_
|
||
|
||
In this essay we will look at two different types of decoupling in the context of web applications:
|
||
|
||
* Decoupling at the _application level_ via a generic JSON Data API
|
||
* Decoupling at the _network architecture level_ via a hypermedia API
|
||
|
||
We will see that, at the application level, a hypermedia API tightly couples your front-end and back-end. Despite this
|
||
fact, surprisingly, the hypermedia API is in fact more resilient in the face of change.
|
||
|
||
## Coupling
|
||
|
||
[Coupling](https://en.wikipedia.org/wiki/Coupling_%28computer_programming%29) is a property of a software system in which
|
||
two modules or aspects of the system have a high degree of interdependence. _Decoupling_ software is the act of reducing this
|
||
interdependence between unrelated modules so that they can evolve independently of one another.
|
||
|
||
The concept of coupling and decoupling is closely (and inversely) related to
|
||
[cohesion](https://en.wikipedia.org/wiki/Cohesion_(computer_science)). Highly cohesive software has related logic
|
||
within a module or conceptual boundary, rather than spread out throughout a codebase. (A related concept is our own idea
|
||
of [Locality of Behavior](/essays/locality-of-behaviour/))
|
||
|
||
Broadly, experienced developers strive for decoupled and cohesive systems.
|
||
|
||
## JSON Data APIs - Application Level Decoupling
|
||
|
||
A common approach to building web applications today is to create a JSON Data API and then consume that JSON API using
|
||
a JavaScript framework such as React. This application-level architectural decision decouples the front-end code
|
||
from the back-end code, and allows the reuse of the JSON API in other contexts, such as a mobile applications, 3rd
|
||
party client integrations, etc.
|
||
|
||
This is an _application-level_ decoupling because the decision and implementation of the decoupling is done by the
|
||
application developer themselves. The JSON API provides a "hard" interface between the two pieces of software.
|
||
|
||
Using my favorite example, consider a simple JSON for a bank that has a `GET` end point at `https://example.com/account/12345`.
|
||
This API might return the following content:
|
||
|
||
```json
|
||
HTTP/1.1 200 OK
|
||
|
||
{
|
||
"account": {
|
||
"account_number": 12345,
|
||
"balance": {
|
||
"currency": "usd",
|
||
"value": -50.00
|
||
},
|
||
"status": "overdrawn"
|
||
}
|
||
}
|
||
```
|
||
|
||
This Data API can be consumed by any client: a web application, a mobile client, a third party, etc. It is, therefore
|
||
decoupled from any particular client.
|
||
|
||
### Decoupling Via A JSON API In Practice
|
||
|
||
So far, so good. But how does this decoupling work out in practice?
|
||
|
||
In our essay [Splitting Your Data & Application APIs: Going Further](https://htmx.org/essays/splitting-your-apis/) you
|
||
will find the following quote:
|
||
|
||
> The worst part of my job these days is designing APIs for front-end developers. The conversation goes inevitably as:
|
||
>
|
||
> Dev – So, this screen has data element x,y,z… could you please create an API with the response format {x: , y:, z: }
|
||
>
|
||
> Me – Ok
|
||
>
|
||
> Jean-Jacques Dubray - <https://www.infoq.com/articles/no-more-mvc-frameworks>
|
||
|
||
This quote shows that, although we have driven coupling out with a pitchfork (or, in our case, with a JSON API) it has come
|
||
back through requests for web application-specific JSON API end points. These sorts of requests end up recoupling the
|
||
front-end and back-end code: the JSON API is no longer providing a generic JSON Data API, but rather a specific API for
|
||
the front-end needs.
|
||
|
||
Worse, these front-end needs will often change frequently as your application evolves, necessitating the modification
|
||
of your JSON API. What if other non-web application clients have come to depend on the original API?
|
||
|
||
This problem leads to the "versioning hell" that many JSON Data API developers face when supporting both web applications as well
|
||
as other non-web application clients.
|
||
|
||
#### A Solution: GraphQL
|
||
|
||
One potential solution to this problem is to introduce [GraphQL](https://graphql.org/), which allows you to have a much
|
||
more expressive JSON API. This means that you don't need to change it as often when your API client's needs change.
|
||
|
||
This is a reasonable approach for addressing the problem outlined above, but there are problems with it. The biggest
|
||
issue that we see is security, as we outline this in [The API Churn/Security Trade-off](https://intercoolerjs.org/2016/02/17/api-churn-vs-security.html) essay.
|
||
|
||
Apparently facebook uses a [whitelist](https://twitter.com/AdamChainz/status/1392162996844212232) to deal with the security
|
||
issues introduced by GraphQL, but many developers who are using GraphQL appear to not understand the security threats
|
||
involved with it.
|
||
|
||
#### Another Solution: Splitting Your Application & General Data APIs
|
||
|
||
Another approach recommended by [Max Chernyak](https://max.engineer/) in his article
|
||
[Don’t Build A General Purpose API To Power Your Own Front End](https://max.engineer/server-informed-ui), is to build
|
||
*two* JSON APIs:
|
||
|
||
* An application specific JSON API that can be modified as needed
|
||
* A general purpose JSON API that can be consumed by other clients such as mobile, etc.
|
||
|
||
This is a pragmatic solution to address what appears to be the _inherent_ coupling between your web application's front-end
|
||
and the back-end code supporting it, and it doesn't involve the security tradeoffs involved in a general GraphQL API.
|
||
|
||
## Hypermedia - Network Architecture Decoupling
|
||
|
||
Now let us consider how a _hypermedia API_ decouples software.
|
||
|
||
Consider a potential response to the same `GET` for `https://example.com/account/12345` that we saw above:
|
||
|
||
```html
|
||
HTTP/1.1 200 OK
|
||
|
||
<html>
|
||
<body>
|
||
<div>Account number: 12345</div>
|
||
<div>Balance: $100.00 USD</div>
|
||
<div>Links:
|
||
<a href="/accounts/12345/deposits">deposits</a>
|
||
<a href="/accounts/12345/withdrawals">withdrawals</a>
|
||
<a href="/accounts/12345/transfers">transfers</a>
|
||
<a href="/accounts/12345/close-requests">close-requests</a>
|
||
</div>
|
||
<body>
|
||
</html>
|
||
```
|
||
|
||
(Yes, this is an API response. It just happens to be a hypermedia-formatted response, in this case HTML.)
|
||
|
||
Here we see that, at the application level, this response could not be more tightly coupled to the "front-end". In fact,
|
||
it *is* the front-end, in the sense that the API response specifies not only the data for the resource, but also provides
|
||
layout information on how, exactly, to display this data to the user.
|
||
|
||
The response also contains _hypermedia controls_, in this case, links, that an end user can select from to continue
|
||
navigating the hypermedia API that this [Hypermedia-Driven Application](https://htmx.org/essays/hypermedia-driven-applications/) provides.
|
||
|
||
So, where is the decoupling in this case?
|
||
|
||
### REST & The Uniform Interface
|
||
|
||
The decoupling in this case is occurring at a _lower level_. It is happening at the _network architecture_ level, which
|
||
is to say, at the system level. [Hypermedia systems](https://hypermedia.systems) are designed to decouple the hypermedia
|
||
client (in the case of the web, the browser) from the hypermedia server.
|
||
|
||
This is accomplished primarily via the Uniform Interface constraint of REST and, in particular, by using
|
||
Hypermedia As The Engine of Application State ([HATOEAS](/essays/hateoas)).
|
||
|
||
This style of decoupling allows tighter coupling at the higher application level (which we have seen may be an
|
||
_inherent_ coupling) while still retaining the benefits of decoupling for the overall system.
|
||
|
||
### Decoupling Via Hypermedia In Practice
|
||
|
||
How does this sort of decoupling work in practice? Well, let's say that we wish to remove the ability to transfer money
|
||
from our bank to other banks as well as the ability to close accounts.
|
||
|
||
What does our hypermedia response for this `GET` request now look like?
|
||
|
||
```html
|
||
HTTP/1.1 200 OK
|
||
|
||
<html>
|
||
<body>
|
||
<div>Account number: 12345</div>
|
||
<div>Balance: $100.00 USD</div>
|
||
<div>Links:
|
||
<a href="/accounts/12345/deposits">deposits</a>
|
||
<a href="/accounts/12345/withdrawals">withdrawals</a>
|
||
</div>
|
||
<body>
|
||
</html>
|
||
```
|
||
|
||
You can see that in this response, links for those two actions have been removed from the HTML. The browser simply
|
||
render the new HTML to the user. To a rounding error, there are no clients sitting around using the _old_ API. The
|
||
API is encoded within and discovered through the hypermedia.
|
||
|
||
This means that we can dramatically change our API without breaking our clients.
|
||
|
||
This flexibility is the crux of the REST-ful network architecture and, in particular, of [HATEOAS](/essays/hateoas/).
|
||
|
||
As you can see, despite much tighter _application-level_ coupling between our front-end and back-end, we actually have
|
||
more flexibility due to the _network architecture_ decoupling afforded to us by the Uniform Interface aspect of
|
||
REST-ful [hypermedia systems](https://hypermedia.systems).
|
||
|
||
### But That's A Terrible (Data) API!
|
||
|
||
Many people would object that, sure, this hypermedia API may be flexible for our web application, but it makes for a
|
||
terrible general purpose API.
|
||
|
||
This is quite true. This hypermedia API is tuned for a specific web application. It would be cumbersome and error-prone
|
||
to try to download this HTML, parse it and try to extract information from it. This hypermedia API only makes sense as part
|
||
of a larger hypermedia system, being consumed by a proper hypermedia client.
|
||
|
||
This is exactly why we recommend creating a general purpose JSON API alongside your hypermedia API in
|
||
[Splitting Your Data & Application APIs: Going Further](https://htmx.org/essays/splitting-your-apis/). You can
|
||
take advantage of the flexibility of hypermedia for your own web application, while providing a
|
||
general purpose JSON API for mobile applications, third party applications, etc.
|
||
|
||
(Although, we should mention, a [hypermedia-based mobile application](https://hyperview.org) might be a good choice too!)
|
||
|
||
## Conclusion
|
||
|
||
In this essay we looked at two different types of decoupling:
|
||
|
||
* Application level decoupling via a JSON Data API
|
||
* Network-architecture decoupling via REST/HATEOAS in a hypermedia system
|
||
|
||
And we saw that, despite the tighter application-level coupling found in a hypermedia-based application, it is the
|
||
hypermedia system that handles changes more gracefully.
|