Let's say I have something like this
Controller
uses Service
.Service
has History
, Source
and HttpClient
.Source
has SourceRepository
and id
.
Source
is only useful for other objects after fetching information from SourceRepository
.History
has HistoryRepository
and Source
.Below is a mixture of PHP + pseudocode (for the sake of simplicity) to ilustrate this scenario.
public class Source
{
...
public function __construct(InterfaceRepository $repository, int $id)
{
$this->data = $this->repository->findById($id);
}
}
public class History
{
...
public function __construct(InterfaceRepository $repository, InterfaceSource $source)
{
...
}
public function hasHistory(): bool
{
return $this->repository->exists($this->source->data);
}
public function duplicateHistory()
{
...
}
}
public class Service
{
...
public function __construct(InterfaceHistory $history, InterfaceSource $source, InterfaceHttpClient $httpClient)
{
...
}
public function send()
{
if ($this->history->hasHistory()) {
return $this->history->duplicateHistory();
}
return $this->sendNewRequest();
}
public function sendNewRequest()
{
$this->httpClient->postRequest($this->source->data);
}
}
public class Controller
{
public function doSomething(int $id)
{
$sourceRepository = new SourceRepository();
$source = new Source($sourceRepository, $id);
$historyRepository = new HistoryRepository();
$history = new History($historyRepository, $source);
$httpClient = new Guzzle();
$service = new Service($history, $source, $httpClient);
$service->send();
}
}
Now I have some questions regarding Dependency Injection:
Controller
?
Source
because of id
. How should I solve this?After reading more about Dependency Injection, Inversion of Control and Composition Root (quite awesome read by the way, suggested by @Steven), I understood what has to be done in my case.
So regarding my questions:
Should all object building really stay in the highest level, in this case the Controller?
The part about highest level is correct, the rest isn't. The best place to do the object building, also called object graph is in the Composition Root, which is very close to the entrypoint of the application and/or specific route.
Composition Root is a logical layer and it's only responsibility is composing the object graph. It may be a separate class and/or function. Although it may be in the same file as another thing, it is still a separate layer. (Read this link again).
So in my case, what I'll do is before I get to the controller, I'll create a separate Composition class that will create everything necessary and inject only the Service
to the Controller
, so it can call $service->send()
.
If I was to use a Dependency Injection Container, I think it wouldn't be able to instantiate Source because of id. How should I solve this?
Incorrect. Dependency Injection Containers do have a way to instantiate classes with dynamic parameters such as scalar values or other.