Skip to content

Project Structure

A Marko application has a flat, predictable structure. Modules live in three locations with clear priority rules, and every module follows the same internal layout.

my-app/
├── app/ # Application modules (highest priority)
│ ├── blog/
│ │ ├── src/
│ │ ├── config/
│ │ ├── composer.json
│ │ └── module.php # Optional module configuration
│ └── shop/
├── modules/ # Third-party modules (medium priority)
├── vendor/ # Composer packages (lowest priority)
│ └── marko/
│ ├── core/
│ ├── routing/
│ └── ...
├── public/
│ └── index.php # Entry point
├── config/ # Root configuration
├── storage/ # Logs, cache, sessions
├── composer.json
└── .env

Marko discovers modules from three locations. When conflicts arise, higher-priority locations win:

app/ — Application Modules (Highest Priority)

Section titled “app/ — Application Modules (Highest Priority)”

Your custom modules live here. These override everything else. Each subdirectory is a module:

app/
├── blog/ # Your blog customizations
├── checkout/ # Your checkout logic
└── analytics/ # Your analytics module

modules/ — Third-Party Modules (Medium Priority)

Section titled “modules/ — Third-Party Modules (Medium Priority)”

For manually-installed modules that aren’t on Packagist:

modules/
└── acme/
└── custom-shipping/

vendor/ — Composer Packages (Lowest Priority)

Section titled “vendor/ — Composer Packages (Lowest Priority)”

All composer require’d packages. Marko auto-discovers modules with marko.module: true.

Every module follows the same structure:

my-module/
├── src/ # PHP source code (PSR-4 autoloaded)
│ ├── Controller/ # HTTP controllers
│ ├── Model/ # Domain models / entities
│ ├── Repository/ # Data access
│ ├── Observer/ # Event observers
│ └── Plugin/ # Method plugins
├── config/ # Module configuration files
├── database/
│ ├── migrations/ # Schema migrations
│ └── seeders/ # Data seeders
├── resources/
│ └── views/ # View templates
├── tests/ # Module tests
├── composer.json # Package definition
└── module.php # Optional: bindings, singletons, plugins, observers

This optional file declares a module’s bindings, singletons, and wiring:

module.php
<?php
declare(strict_types=1);
use Marko\Cache\CachePoolInterface;
use Marko\Cache\File\FileCachePool;
return [
'bindings' => [
CachePoolInterface::class => FileCachePool::class,
],
'singletons' => [
FileCachePool::class,
],
];

The web entry point is minimal — it boots the framework and handles the request:

public/index.php
<?php
declare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use Marko\Core\Application;
$app = Application::boot(dirname(__DIR__));
$app->handleRequest();