Import
Every BEAR application is a collection of resources, each with a URI. Because of this, you can install a whole application as a “part” of another BEAR application and use it through the same interface as your own resources. It splits a system into independent applications the way microservices do, while calls stay in-process—no network involved.
Composer install
Install the BEAR application you want to use as a composer package. 1
composer.json
{
"require": {
"bear/package": "^1.13",
"my-vendor/weekday": "dev-master"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/bearsunday/tutorial1.git"
}
]
}
Module install
Register the application to import with ImportAppModule. ImportApp takes three arguments: a hostname, the application name (namespace), and a context.
AppModule.php
+use BEAR\Package\Module\ImportAppModule;
+use BEAR\Package\Module\Import\ImportApp;
class AppModule extends AbstractAppModule
{
protected function configure(): void
{
// ...
+ $this->install(new ImportAppModule([
+ new ImportApp('foo', 'MyVendor\Weekday', 'prod-app')
+ ]));
$this->install(new PackageModule());
}
}
foo— the hostname that points to the import target, corresponding toapp://foo/in a URI.MyVendor\Weekday— the application name (namespace) to import.prod-app— the context the imported application runs under.
Notice that the context can be specified independently of the host application’s own context. Even while your own application runs in a development context, the imported application can run under prod-app bindings. This kind of composition is possible precisely because each BEAR application has a self-contained DI and AOP configuration. 2
Request
Request an imported resource by specifying the hostname you registered.
class Index extends ResourceObject
{
use ResourceInject;
public function onGet(string $name = 'BEAR.Sunday'): static
{
$weekday = $this->resource->get('app://foo/weekday?year=2022&month=1&day=1');
$this->body = [
'greeting' => 'Hello ' . $name,
'weekday' => $weekday
];
return $this;
}
}
The only difference from your own resources (app://self/) is the hostname—#[Embed] and #[Link] work the same way. Calling code doesn’t need to know whether a resource is its own or imported, and although this request looks like remote access, it carries none of the latency or connection-failure cost of a distributed system.
Requests from other systems
It’s just as easy to use BEAR resources from other frameworks or CMSs. Install the application as a package the same way, then get an injector by passing Injector::getInstance the application name, context, and application path, and get a resource client from it. No bootstrap script is needed—the BEAR application functions as a single library configured for the context you specify.
use BEAR\Package\Injector;
use BEAR\Resource\ResourceInterface;
$resource = Injector::getInstance(
'MyVendor\Weekday',
'prod-api-app',
dirname(__DIR__) . '/vendor/my-vendor/weekday'
)->getInstance(ResourceInterface::class);
$weekday = $resource->get('/weekday', ['year' => '2022', 'month' => '1', 'day' => 1]);
echo $weekday->body['weekday'] . PHP_EOL;
Environment variables
Environment variables are global and shared across applications, so prefix them to avoid conflicts. Rather than a .env file, the imported application picks up shell environment variables just as it would in production.
System boundary
Building a large application as a collection of smaller ones is similar to microservices, but without microservices’ downsides, such as increased infrastructure overhead. And because the boundary is the application itself rather than a namespace convention, component independence and boundaries are clearer than with a modular monolith.
The code for this page can be found at bearsunday/example-import-app.
Multilingual framework
Using BEAR.Thrift, you can access resources from other languages, different PHP versions, or other BEAR applications via Apache Thrift.