User Tools

Site Tools


z_appendix_101:php_factories_in_the_ai_era

14. Factories — When Object Creation Deserves a Home

By now, we’ve seen how our domain is shaped:

  • Value Objects carry meaning.
  • Entities carry identity.
  • Services coordinate behavior between objects.
  • Repositories store and retrieve entities.

But there’s one more question that appears naturally as our code grows:

  • Where should
    object 'creation'
    live?

At first, it seems obvious:
“Just use the keyword new inside the constructor or service.”

But as soon as creation becomes even slightly complex —
multiple parameters, validation, defaults, dependencies, or branching logic
— we start to feel the friction.

This is where factories come in.

A factory reveals intention.


1. What a Factory Really Is

A factory is simply:

A named place for object creation
when
new is no longer enough.

In PHP, a factory isn't a complex academic concept
—it’s just a dedicated spot for our setup logic to live.

Factories exist for the same reason Services exist:

  • to keep our domain expressive
  • to keep our objects small and intentional
  • to keep our code readable to humans (and AI)

2. When new Is Enough

If our object is simple:

$user = new User($name, $email);

That’s fine. No factory needed.

Factories are not mandatory.
They are not “best practice”.
They are just an optional tool for when creation becomes a concept.


3. So, When does new Start to Feel Wrong?

Factory creation becomes a useful concept when:

  • the constructor has too many parameters
  • the parameters need validation
  • the object needs defaults or derived values
  • the object depends on other objects
  • the creation process has a name (“register”, “publish”, “open”, “schedule”)
  • we want to hide implementation details
  • we want to avoid leaking Value Object construction everywhere

When we feel this friction,
a factory gives the creation logic
a home
.


4. A Gentle Example

Imagine creating a User entity:

class User {
    public function __construct(
        public string $id,
        public string $name,
        public Email $email,
        public DateTimeImmutable $createdAt
    ) {}
}

Creating a user now requires:

  • generating an ID
  • validating an email
  • creating a DateTime
  • assembling everything in the right order

We could do this in a controller or service:

$user = new User(
    Uuid::uuid4()->toString(),
    $name,
    new Email($email),
    new DateTimeImmutable()
);

But this is noisy.
It mixes concerns.
It leaks implementation details everywhere.

A factory gives this process a name:

class UserFactory {
    public function register(string $name, string $email): User {
        return new User(
            Uuid::uuid4()->toString(),
            $name,
            new Email($email),
            new DateTimeImmutable()
        );
    }
}

And now the creation reads like a story:

$user = $userFactory->register($name, $email);

That’s the whole point.


5. Factories and Services Are Not the Same

A service performs an action in our domain:

$ticketAssignmentService->assign($ticket, $user);

A factory creates something in our domain:

$user = $userFactory->register($name, $email);

Factories don’t coordinate behavior.
They don’t store anything.
They don’t perform business rules.

They simply create objects with intention.


6. When to Use a Factory

Use a factory when:

  • creation has a name
  • creation has rules
  • creation has steps
  • creation has dependencies
  • creation has meaning

If creation is trivial, skip the factory.
If creation is a concept, give it a home.


7. The Mental Model

A factory is:

  • a named place for creation
  • a way to hide complexity
  • a way to keep constructors simple
  • a way to keep services focused
  • a way to make our domain read like a story

Factories help our code say what it means.


8- When to Ask AI for a Factory — A Conversation Example

Sometimes we don’t realize we need a factory until we start talking to AI.
Here’s what that moment looks like in practice.

1. We start with a simple request

“Create a User entity with name, email, and createdAt.”

AI gives us something like:

class User {
    public function __construct(
        public string $id,
        public string $name,
        public Email $email,
        public DateTimeImmutable $createdAt
    ) {}
}

So far, so good.

2. Then we ask AI to show how to create one User

“Show me how to create a new User.”

AI responds with something like:

$user = new User(
    Uuid::uuid4()->toString(),
    $name,
    new Email($email),
    new DateTimeImmutable()
);

And this is the moment where our judgment kicks in.

We look at that creation code and think:

  • That’s a lot of steps.
  • That’s a lot of noise.
  • That’s not something I want repeated everywhere.
  • This creation process feels like a concept.

This is the moment to ask for a factory.

3. We ask AI to give creation a home

“This creation logic is too noisy.
Give me a factory that handles user registration.”

AI now produces something like:

class UserFactory {
    public function register(string $name, string $email): User {
        return new User(
            Uuid::uuid4()->toString(),
            $name,
            new Email($email),
            new DateTimeImmutable()
        );
    }
}

And suddenly the creation reads like a story:

$user = $userFactory->register($name, $email);

This is the moment when the domain becomes clearer —
not because of the code,
but because of the conversation.

Factories are about clarity.
When creation becomes a story,
give it a name
.


Summary

Factories are not about abstraction — they’re about clarity. When creation becomes a concept, naming it makes our domain easier to read, easier to teach, and easier to collaborate with AI. We’re not adding structure. We’re revealing intention.


Tony de Araujo —New York


z_appendix_101/php_factories_in_the_ai_era.txt · Last modified: by editor