Not bad. We just made a dynamic webpage without meddling in the world of Javascript, NPM, WebPack, Vue, React, Babel, etc. Maybe there’s something to this Livewire thing after all?

What Livewire Does Wrong

So, now we know what livewire is and what it does. So… why did I say it was dangerous earlier?

Livewire’s faults can be broken down into the following categories:

Livewire violates the separation of concerns

Livewire violates the single-responsibility principle

Livewire complicates the development workflow

Livewire is inefficient

Livewire is difficult to test

Livewire is inherently imperative, whereas user interfaces are inherently declarative.

Livewire is an unfortunate victim of Conway’s Law

Livewire has technical misgivings

Liveware has a lack of support, and probably always will

This might take a while, but I want to go over these one-by-one and really hit the point home HOW Livewire falls short and WHY it’s a problem.

Livewire violates Separation of Concerns

Separation of Concerns is a principle that says that software should be split up into “chunks”, where each chunk worries about a different “concern”.

In a common web application, you have layers. So:

The database layer is the core of the application, and knows NOTHING about what’s above it.

The data-access-layer (DAL) reads from the Database, and knows ONLY about the database layer

The ‘Model Layer’ is largely pure. It knows ONLY about the “Data Transfer Objects” (or DTO’s) which are returned from the Data-Access-Layer.

Author’s Note: “pure” in this context means that its state is 100% determined by the parameters it receives. Or “props”, to use React parlance.

The ‘Service Layer’ or ‘Business Layer’ knows ONLY of the Model layer, and the Data-Access-Layer. This is where the “Logic” of the application happens. But if situations arise it’s permissible if it knows about the database layer.

The “View Model Layer” is, again, pure. It is fed entirely through parameters from the service layer.

The “Presentation” layer is the highest possible layer. It, theoretically, should be able to know about all the layers below it. In practice, is should only interact with the View Model Layer. This is a core concept of MVVM. It has a finite list of states which may or may not change exactly what is displayed.

Example

You are looking at a recipe online.

The recipe came from a RecipeViewModel.php , which was hydrated by the RecipeController , which got its data from RecipeRepository.php , which used Eloquent to query the Database layer. When you click “Like” on the website, it submits a request to RecipeController , which calls RecipeRepository , which ultimately uses Eloquent to increment a like counter in the Database. The Presentation Layer’s state is updated to that of an incremented like.

How does Laravel Livewire violate Separation of Concerns?

Laravel Livewire requires that the backend keeps track of the presentation layer’s state. If you were writing a “Counter” component, which keeps track of clicks, an action in the Presentation Layer would rely on state in the layer before it (the Controller / View Model layer).

This intermixes the responsibilities of the layers. It means that the previous layer (the View-Model layer) is dictating how something should be presented. It’s tracking state and data that was created in the presentation layer. It’s violating separation of concerns because the layers have been walked backwards.

It’s also incredibly likely that the Livewire Component will need to query for more data, depending on what happens. This is reaching backwards for a previous layer as well. It breaks the purity of the component even further, since its state may rely on the previous layer’s information.

For instance, in the Livewire documentation, they have this example:

public function mount()

{

$this->message = 'Hello ' . auth()->user()->first_name;

}

This shows the Livewire component (which is essentially a view-model) querying for the currently authenticated user. This relies on global state, which will also make testing it a lot harder, but we’ll talk about that later. Also, remember that as I said earlier, view-models are meant to be pure. They, ideally, have no knowledge of any layer before it. That’s the controller’s job.

To illustrate why this is really a problem, think about Custom Polymorphic Types in Laravel Eloquent. When a default polymorphic type is used in the database, it requires that a PHP class name is stored in the database. Layer 0 (the database) is requiring knowlege of layer 2–3 (the data access layer, or the model layer). If we change the name of the class, it breaks the database.

In Laravel Livewire, if we change the presentation layer, it might actually introduce bugs in our View-Model layer.

A bug should only be able to affect the layers which come after it. This is not the case in Laravel Livewire.

Livewire violates the single-responsibility principle

In a similar vein, Laravel Livewire is also guilty of violating two of the SOLID principles: the Single Responsibility Principle being one. In the Unix Philosophy, this was called “Do One Thing and Do It Well” (DOTADIW). Basically, each component of a software system should have one (and ONLY one) role.

However, Livewire components do many things. They control logic and transformations of data. They are stateful by design, so track state. They also control how the data within them looks.

Let’s take an example and compare React and Livewire, with regard to the single-responsibility principle . In both cases, we want to show a Recipe that can be liked.

LIVEWIRE

In Livewire, you would have a RecipeControler , which passes data to recipe.blade.php , which invokes a Livewire component Recipe.php with data.

The component renders HTML that displays the recipe and like button.

When you click Like , the Livewire component accesses the Data Access Layer to increment the like count.

It updates its own internal state, decides how it’s supposed to look, and then re-renders its HTML.

Notice how many things that one component decided to do on its own.

REACT + REDUX

Now, this exact same interaction in React+Redux would look something like this:

An action is dispatched in the presentation layer.

Middleware decides that it cares about the action, and makes a stateless API call which returns a Recipe view-model from the Controller.

Another Action is dispatched in the Presentation layer with the data.

A reducer decides that it cares about that type of action (and only that type of action). The reducer updates the state.

A selector in the container React component updates its internal component state.

The now-updated state is declarative, so it automatically trickles down into the child component.

The user clicks Like

A callback from the container component dispatches another Action

Middleware decides it cares about that action, and dispatches another stateless API call.

The response comes, and flows through redux as an Action again.

The reducer decides it cares about that Action , and updates state.

Again, the change flows to the container via the selector , and finally to the child component.

So… There’s a lot more steps in React+Redux, but notice how each actor along the way does one (and only one) thing.

Imagine the difference between debugging the Laravel Livewire version and the React+Redux version.

In Laravel Livewire, you click ‘Like’ and the display is wrong. It could be the fault of:

The markup in the blade file(s)

The state-updating logic in the Livewire component

The business logic in the data-access layer

Some issue with the global state of the backend, if it must query for other information

You really don’t have any idea where the bug is occurring.

Contrasting that with React+Redux, where you know exactly where the problem is occurring. The view is messed up? the markup in the presentational component is wrong. The state in the component doesn’t update when an item is liked? The selector isn’t selecting properly. The STATE itself isn’t updating, but the action was dispatched? There has to be a problem with the reducer.

Laravel Livewire, in essence, is acting as a swiss-army knife here. It’s attempting to do everything. It simplifies things in the process, but it definitely violates the single-responsibility principle. And when something goes wrong, we say “something’s wrong with the swiss-army knife”, not “something’s wrong with the phillips-head screwdriver”.

Livewire complicates the development workflow

This is a very practical negative side-effect that using Laravel Livewire will have on your workflow.

In a single-page application, the presentational client(s) and the backend service(s) which support them can be developed in unison. The team can agree on an API and begin developing the frontend and backend side-by-side.

With Laravel Livewire, the lines between backend and frontend are blurred. Since the backend concerns itself with frontend state, it must be developed as one unit.

In situations where the team is comprised of soley full-stack developers, the effect of this might not be too bad. They probably already develop features as one deliverable unit. But as the project grows, this restriction can provide some real pain. It also leads to the next point:

Livewire is inefficient

This doesn't matter so much in 2020, where bandwidth is mostly fast and cheap. However, the reality of the situation is that Livewire makes AJAX Requests for every. single. thing. that you do.

It also makes Livewire the polar opposite of a Progressive Web App. If you lose connection, the most basic functions of your app’s presentation layer will cease to function. Modals won’t open, dropdowns won’t… drop down, et cetera. In Livewire’s defense here, it does provide functionality that helps with this. It makes it easy to alert the user that the app will be malfunctioning, but it will still malfunciton.

It also replaces entire chunks of HTML at a time, and forces the browser to re-draw it. Depending on how large your Livewire component is, this could actually make a noticeable difference to the user. In contrast, frameworks like Vue and React use a “Virtual Dom”, and a highly efficient diffing algorithm in order to make their updates. And they cut out the network call completely.

Livewire is inherently imperative, whereas user interfaces are inherently declarative.

As stated in the Livewire Documentation, Livewire is NOT Reactive. This is a gigantic difference between it and React/Vue/Marko/Angular et all.

There’s a reason why so many frameworks are declaritive. User interfaces are declaritive by their very nature. You define what something is, not what it does. The fact that the element showing the quote gets its innerHTML DOM attribute set to the value of the new quote is an implementation detail. We shouldn’t have to tell it how to update the quote. We just tell it, “Here’s where the quote goes. Here’s how it looks.”. And then we write code about changing the quote itself. The framework handles the rest.

You might think that Livewire is “declaritive enough”. I mean, you’re not manually updating DOM attributes.

But the second you start nesting Livewire views, its declaritive facade falls apart.

Imagine you have a Livewire component, that is an input and an error message.

You want a child component to update a parent component’s state? YOU CAN’T.

You want a parent component to pass down a state change to a child component? YOU CAN’T.

You can’t even pass down a closure from the parent to the child, because Livewire needs primitive types and arrays.

If you really want to do one of those things, you need to use Livewire Events. Which means that the one of the components needs to be privy of the implementation details of the other in order to update its properties. This violates separation of concerns again.

That means if you have a tree of 3 nested components, where maybe some are lists of components (i.e generated in loops), it’s going to be inconvenient to make a working UI.

Imagine, for instance, this scenario:

Create an application which displays the letters of the alphabet, and if you click a letter it goes away

Well damn, that seems pretty easy in React. Here’s the full solution:

import React, {useState} from 'react';



export const AlphabetBox = ({isVisible, letter, onLetterClick}) => isVisible && (<span onClick={onLetterClick}>{letter}</span>);



export const AlphabetContainer = ({}) => {

let [blacklist, setBlacklist] = useState([]);



const [addToBlacklist, removeFromBlacklist, isInBlacklist] = [

letter => setBlacklist([...blacklist, letter]),

letter => setBlacklist(blacklist.filter(item => item !== letter)),

letter => blacklist.includes(letter)

];



return 'abcdefghijklmnopqrstuvwxyz'.split('').map(letter => {

return <AlphabetBox onLetterClick={() => addToBlacklist(letter)} isVisible={!isInBlacklist(letter)} letter={letter} />

});

};

Now what about Livewire?

Well, each letter would have to dispatch a Livewire event to the parent. The parent would then have to dispatch a livewire event that somehow each letter component listens for, and knows whether or not it is the one who should update. There are no higher-order components so this becomes harder.

The value that’s passed to it from the “Alphabet Container” Livewire component doesn’t matter. As soon as there’s user interaction, all of the displayed data is coming from the payload of events.

It’s important to note that at this point, the parent has lost all control of the children. Their states will match only because they both independently updated them the same way. However, a slight gust of wind and these states could de-synchronize. That’s just straight-up impossible in React.

Livewire has a lack of support, and probably always will to some degree

This is to be expected from a beta product, and not really a gripe. It’s just a reality of where Livewire is right now. No tooling supports it, so things like syntax highlighting doesn’t exist.

BUT, something unique to Livewire is that some types of tooling will never be available to it. At least, not without a ton of work.

In Vue or React, I can examine every event, and I can see the payloads. I can see the current state of all components in the system. I can rewind, replay, or alter history events in Redux / VueX. Since the state is kept server-side in Laravel Livewire, this will never be possible, unless some type of driver is built to broadcast this information to the frontend, or some server-side dashboard is built. The best we can do is analyze the result of the render function and try to anticipate how we got that result.

Laravel falls victim to Conway’s Law

Finally, some of Livewire’s technical misgivings break some of the most powerful frontend design patterns and techniques.

It also falls victim to Conway’s Law, which (to paraphrase) states that a system is bound exhibit properties of its host system. For instance, an OCAML library is bound to present ideas which resemble OCAML itself, but possibly a more limited version.

The problem is, Livewire is attempting to cross an air-gap. So, all-of-a-sudden the way that you’re designing Presentational components represents PHP.

The primary manifestation of this is the fact that Livewire can only use primitive types, and arrays. This means that one of the most common representations of data in the frontend (the Object) is inaccessible to Livewire. You can emulate it with arrays, but it might be difficult.

You can’t pass in functions, or generate Livewire components on-the-fly, so things like Higher-Order Components cannot be done with Livewire. It can’t be over-emphasized how much of a loss this is. Higher-order components are incredibly powerful, and things like PHP Traits just can’t replace them. I mean, there’s no such thing as a parameterized trait.

So… Why are people excited about Livewire if it presents all these problems?

Well, this is actually a super interesting question. Again, Livewire’s website’s first sentence says “JavaScript is crazy these days”. And notoriously, PHP has some of the world’s most pragmatic programmers. “Don’t make it pretty, just make it work.”, “Pretty” here meaning “easy-to-maintain”.

In my opinion, people are invested in Livewire because it is the fairy-tale they want to come true. The backend is the final source of truth. Only one skillset is needed to develop these applications.

But, today that’s just simply not reality. If you want to follow best practices in a modern application, you need:

Continuous Integration for optimal teamwork

for optimal teamwork Continuous Deployment for optimal workflow

for optimal workflow Cloud-Based environment for optimal uptime, maintainability, and speed.

environment for optimal uptime, maintainability, and speed. Containerized solutions for optimal testing and reproducability. No “it doesn’t happen on my machine” problems.

solutions for optimal testing and reproducability. No “it doesn’t happen on my machine” problems. Possibly an orchestrator , like ECS or Kubernetes, or even Serverless computing, for optimal performance and control in the cloud-first world

, like ECS or Kubernetes, or even Serverless computing, for optimal performance and control in the cloud-first world A declaritive, reactive frontend framework for dynamic content

for dynamic content A backend web-service

A database, whether or not it’s NoSQL, a Graph database, or relational

Is there a way to fix Livewire?

Laravel Livewire has a good idea, just backwards. Intertia.js is a library that has a similar idea, but the frontend ultimately holds the presentational state. After the initial page-load, it works exactly like a single-page application in React+Redux would.

These frameworks strive to create a soltuion for people who don’t want to create API endpoints for a dynamic frontend to call. Intertia.js just leaves out the part of the backend storing UI state, which I personally believe should be considered harmful. Microsoft would disagree.

Conclusion

So, I want to reiterate: I am not trying to be negative, or to crush the hopes and dreams of Caleb or anyone working on/with his project. I will likely continue using Livewire day-to-day at my job, and not complain about it one bit. I just felt the need to point out that Livewire is potentially dangerous, both from a technical and philosophical point-of-view.

If you can correct something in this article, or you agree / disagree, don’t be afraid to leave a comment. I promise that if there’s an issue with this article I will change it. Thanks for reading!