Paul M. Jones

There are no solutions, only tradeoffs.

Payload-Interop 1.0.0 Released

I am happy to announce that after one change from its public review period, the payload-interop specification has been released as 1.0.0 stable!

From the README:

The Domain Payload Object pattern was first described by Vaughn Vernon at https://vaughnvernon.co/?page_id=40

Whereas a Data Transfer Object provides properties found on domain objects, a Domain Payload Object provides entire domain objects. Its purpose is to transport those domain objects across the architectural boundary from the domain layer to the user interface layer.

The Domain Payload Object is not for intra-domain use. It is only for transporting a domain result across the domain layer boundary back to the calling code, typically the user interface layer.

Further, the Domain Payload Object is independent of any particular user interface. As part of the domain layer, it should have no knowledge of HTTP or CLI contexts.

This project defines only a reading interface, so that any user interface code can know how to get both the result and the status out of the Domain Payload Object.

This project does not define a writing or mutation interface. Creation and manipulation of Domain Payload Objects are core application concerns, not user interface concerns. The domain-specific nature places it outside the scope of an interoperability specification.

Try it out on your next project!


No, Jesus Was Not A Socialist

Christians are commanded in Scripture to love, to pray, to be kind, to serve, to forgive, to be truthful, to worship the one God, to learn and grow in both spirit and character. All of those things are very personal. They require no politicians, police, bureaucrats, political parties, or programs.

“The poor you will always have with you, and you can help them any time you want,” says Jesus in Matthew 26:11 and Mark 14:7. The key words there are you can help and want to help. He didn’t say, “We’re going to make you help whether you like it or not.”

In Luke 12:13-15, Jesus is approached with a redistribution request. “Master, speak to my brother that he divideth the inheritance with me,” a man asks. Jesus replied, “Man, who made me a judge or divider over you?” Then he rebuked the petitioner for his envy.

Christianity is not about passing the buck to the government when it comes to relieving the plight of the poor. Caring for them, which means helping them overcome it, not paying them to stay poor or making them dependent upon the state, has been an essential fact in the life of a true Christian for 2,000 years. Christian charity, being voluntary and heartfelt, is utterly distinct from the compulsory, impersonal mandates of the state.

Via https://www.washingtonexaminer.com/opinion/no-jesus-was-not-a-socialist.


Implicit Bias Is Pseudo-Scientific Nonsense

Via Ted Frank we have this:

The implicit association test ... is an excellent example [of the replication crisis in social "science"]. Banaji and Greenwald claim that the IAT, a brief exercise in which one sits down at a computer and responds to various stimuli, measures unconscious bias and therefore real-world behavior. If you score highly on a so-called black-white IAT, for example, that suggests you will act in a more biased manner toward a black person than a white person. Many social psychologists view the IAT, which you can take on Harvard University’s website, as a revolutionary achievement, and in the 20 years since its introduction it has become both the focal point of an entire subfield of research and a mainstay of diversity trainings all over the country. That’s partly because Banaji, Greenwald, and the test’s other proponents have made a series of outsize claims about its importance for fighting racism and inequality.

The problem, as I showed in a lengthy rundown of the many, many problems with the test published this past January, is that there’s very little evidence to support that claim that the IAT meaningfully predicts anything. In fact, the test is riddled with statistical problems — problems severe enough that it’s fair to ask whether it is effectively “misdiagnosing” the millions of people who have taken it, the vast majority of whom are likely unaware of its very serious shortcomings. There’s now solid research published in a top journal strongly suggesting the test cannot even meaningfully predict individual behavior. And if the test can’t predict individual behavior, it’s unclear exactly what it does do or why it should be the center of so many conversations and programs geared at fighting racism.

From https://nymag.com/intelligencer/2017/12/iat-behavior-problem.html.


Payload-Interop Public Review Period

The payload-interop project defines an interoperable interface for reading the domain result and domain status from a Domain Payload Object, whose purpose is to transport domain objects across the architectural boundary from the domain layer to the user interface layer.

After a long incubation period and much research, the project has finished internal deliberation and is now inviting wider public review. You can go straight to the code here.

Please send your comments, questions, and critiques as Github issues. Pull requests are also welcome.

Thanks, and happy New Year!


Atlas, PostgreSQL, and RETURNING

One of the powerful features of PostgreSQL is its RETURNING clause. For example, if you have a table like this ...

CREATE TABLE articles (
    article_id SERIAL PRIMARY KEY,
    title      VARCHAR(255) NOT NULL,
    -- ...
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

... and you insert a row using Atlas.Query with a RETURNING clause ...

/** @var $insert \Atlas\Query\Insert */
$insert
    ->into('articles')
    ->columns(['title' => 'My Article'])
    ->returning('created_at');

$pdoStatement = $insert->perform();

... then you can fetch the RETURNING column values from the PDOStatement result:

$returning = $pdoStatement->fetch(PDO::FETCH_ASSOC);
var_export($returning);
// ['created_at' => (the postgresql timestamp)]

If you have an Atlas.Table data gateway for that table, you can make use of RETURNING in your TableEvents classes to populate database-provided values into your Row objects automatically.

To do so, add a RETURNING clause in the modifyInsertRow() method, then retrieve the values into the Row in the afterInsertRow() method:

// ...

class ArticleTableEvents extends TableEvents
{
    public function modifyInsertRow(
        Table $table,
        Row $row,
        Insert $insert
    ) : void
    {
        $insert->returning('created_at');
    }

    public function afterInsertRow(
        Table $table,
        Row $row,
        Insert $insert,
        PDOStatement $pdoStatement
    ) : void
    {
        $returning = $pdoStatement->fetch(PDO::FETCH_ASSOC);
        $row->created_at = $returning['created_at'];
    }
}

Now when a Row gets inserted through the Table data gateway, the created_at value will be populated automatically.

/** @var $tableLocator \Atlas\Table\TableLocator */
$articleTable = $tableLocator->get(ArticleTable::CLASS);
$article = $articlesTable->newRow();
$article->title = 'bar';
$articleTable->insertRow($article);
echo $article->created_at; // the postgresql timestamp

You can do the same for updates as well, using modifyUpdateRow() and afterUpdateRow().

Bonus: becuase it uses both Atlas.Table and Atlas.Query, this functionality is available "for free" in Atlas.Orm!


Controllers are Services

tl;dr: Contra my previous opinion, controllers are Service objects; my understanding of a "Service", regarding DI/SL Containers, was incorrect. However, I do continue to opine that it is better use a Factory to get new Controller instances, rather than getting them from the Container directly in a non-Factory object. All that and more in this followup post.


I

My previous post generated a great discussion on Reddit between myself and /u/ahundiak, which I suggest you read in its entirety. To summarize:

  • I thought of Services in a Container as objects that are retained for reuse throughout the application; e.g. a PDO instance, a logger, etc.

  • ahundiak disagreed, and pointed to the Symfony explanation of a Service as "objects that do something", even when not shared throughout the system.

  • I found that explanation unsatisfying and not well-defined.

  • However, ahundiak also said "Symfony only creates service definitions for objects which are actually injected somewhere." That piqued my interest, because it got closer to the heart what I was trying to express.

From there we ventured a bit further afield.

Read more


Controllers are not Services

Controllers (in webbish MVC) and Actions (in ADR) are not Services. That is, their instances are not shared throughout the system. They are not shared objects used in many different places. Each is created and used only once, in one place, usually as the result of routing logic.

As such, Controllers and Actions should not be defined as "Services" in Dependency Injection containers. They should not be in a Service Locator, either.

Instead, try to use a Factory to create the one Controller or Action that you need for the interaction.

UPDATE (7 Dec 2019): Based on the Reddit discussion below, I have reason to revise my opinion. Look for a followup post in the next couple of days.


See the Reddit conversation on this post here.


Teams do not accomplish anything of genuine intellectual value

[T]eams are for normies, for neurotypicals, for trash people who can’t retain multiple levels of variable dereferencing in their heads while coding.

Teams do not accomplish, and have never accomplished, anything of genuine intellectual value. The history of scientific progress is a history of individuals.

Yes, you need a "team" to actually assemble the atomic bomb or the Intel Itanium or a commercial software product. You don’t need a team to conceive it and do the mental heavy lifting.

The effective IQ of a team is the same as the lowest IQ in the team; the productivity of the team is a minor percentage of the productivity you could get from its smartest member working alone.

Every once in a while you will see one brilliant person be inspired by another brilliant person in the near vicinity. This happens once for every hundred million times a "team" crushes the abilities of its members.

Science, even computer science, is not football.

I find myself sympathetic to this outlook. Via Weekly Roundup: The Passion Of Saint iGNUcius Edition.


Speaking at Bulgaria PHP 2019

I mentioned a couple of days ago that, as favors to friends, I am temporarily relaxing my self-imposed restriction on speaking at conferences I have to fly to. The first was EEConf 2019; the second is Bulgaria PHP.

As the opening talk of day 2, I'll be giving a brand-new presentation on "Rethinking What You Think You Know." It's nominally about the road to Action Domain Responder, but I expect to include more than just that. See you there!


Don’t Believe All Women, Because Some Are Terrible

"Maybe we should treat people like people:"

[I]if we are required to believe every woman who makes an accusation, then every allegation, simply by the act of making it, becomes “credible.” This leaves little room for raising questions about honesty and due process, and plenty of space for ideological distortion.

And since ideology abhors nuance, simple narratives come to dominate the discussion when an allegation is made (in Kavanaugh’s case: beer-drinking private school boy = bad; beer-drinking private school girl = good). And anything that contradicts the narrative is explained away or ignored (such as, being drunk is damning evidence if you’re the accused man, but exculpatory if you’re the woman who made the allegation).

Via https://www.commentarymagazine.com/politics-ideas/dont-believe-all-women-because-some-are-terrible/.