mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-09-30 06:21:19 +00:00

Update the static site generator to include the date and author automatically as part of the essays. I made a couple changes to the underlying static structure to make this a little more seamless, including: * New "essay.html" template for posts in `/essays` * Added an author taxonomy (can eventually be author pages) * Added Carson as the author of all the existing esssays * Cleaned up manual date entires, where they existed All this will make it easier to publish guest essays and sort the essays (chronologically, by topic, or by author) as the essay base expands.
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 re-use 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.
|