Magento Module Structure — module.xml, registration.php and Architecture
In this tutorial, you'll learn how Magento modules are structured, how to create a new module with registration.php and module.xml, and how to enable it in your Magento installation.
What You'll Learn
- The complete directory structure of a Magento module
- How to create registration.php and module.xml
- The purpose of each directory: Block, Controller, Model, etc, Setup, view
- How to enable a module with bin/magento commands
- How to configure module dependencies and setup versions
Why It Matters
Everything in Magento is a module. The catalog, sales, checkout, customer management — all are modules. When you write custom functionality, you create a module. When you install a third-party extension, you add a module. Understanding the module structure is the foundation of all Magento development. Without this knowledge, you cannot create custom features, integrate third-party systems, or even debug why an extension is not working.
Real-World Use
A retail chain needs a custom loyalty points system. Customers earn points on every purchase and redeem them at checkout. The system needs custom database tables (Setup), admin configuration (etc/adminhtml), API endpoints (Controller/Api), frontend display (view/frontend), and email notifications (etc/email_templates). All of this lives inside a single module directory at app/code/Vendor/Loyalty/. The module structure organizes each concern into its own subdirectory, keeping the code maintainable as the system grows to handle millions of loyalty transactions.
Learning Path
flowchart LR A["25: Module Structure
You are here"]:::current A --> B["26: Dependency Injection"] B --> C["27: Plugins"] C --> D["28: Observers and Events"] classDef current fill:#38bdf8,color:#0f172a,stroke-width:2px
What Is a Magento Module
A Magento module is a directory containing PHP classes, configuration XML files, and templates that add or modify functionality. Modules are the building blocks of Magento. Every feature you see in the admin panel and storefront comes from a module.
Modules live in app/code/VendorName/ModuleName/. The vendor name is usually your company or personal namespace. The module name identifies the functionality.
A module minimally requires two files to be recognized by Magento:
registration.php— registers the module with Magento's component registryetc/module.xml— declares the module's name, version, and dependencies
Directory Structure
Here is the complete directory structure of a typical Magento module:
app/code/Vendor/Module/
├── registration.php
├── etc/
│ ├── module.xml
│ ├── config.xml
│ ├── di.xml
│ ├── routes.xml
│ ├── events.xml
│ ├── adminhtml/
│ │ ├── di.xml
│ │ ├── routes.xml
│ │ └── events.xml
│ └── frontend/
│ ├── di.xml
│ ├── routes.xml
│ └── events.xml
├── Block/
│ ├── Index.php
│ └── Adminhtml/
│ └── Grid.php
├── Controller/
│ ├── Index/
│ │ └── Index.php
│ └── Adminhtml/
│ └── Index/
│ └── Index.php
├── Model/
│ ├── ResourceModel/
│ │ ├── Example.php
│ │ └── Collection.php
│ └── Example.php
├── Setup/
│ ├── InstallSchema.php
│ ├── InstallData.php
│ └── UpgradeSchema.php
├── view/
│ ├── frontend/
│ │ ├── layout/
│ │ │ └── default.xml
│ │ ├── templates/
│ │ │ └── example.phtml
│ │ └── web/
│ │ ├── js/
│ │ │ └── example.js
│ │ └── css/
│ │ └── example.css
│ └── adminhtml/
│ ├── layout/
│ ├── templates/
│ └── web/
├── i18n/
│ └── en_US.csv
├── Api/
│ ├── Data/
│ │ └── ExampleInterface.php
│ └── ExampleRepositoryInterface.php
└── Test/
├── Unit/
└── Integration/
registration.php
The registration.php file tells Magento's autoloader that a module exists and where to find its classes. Every module must have one.
<?php
use Magento\Framework\Component\ComponentRegistrar;
ComponentRegistrar::register(
ComponentRegistrar::MODULE,
'Vendor_Module',
__DIR__
);
The first argument specifies the component type. For modules, it is always ComponentRegistrar::MODULE. Other options include THEME, LANGUAGE, and LIBRARY.
The second argument is the module name in Vendor_Module format. This must exactly match the module's directory name and the name declared in module.xml.
The third argument is the directory path. __DIR__ refers to the module's root directory. Magento uses this path to resolve class names and locate resource files.
etc/module.xml
The module.xml file declares the module's identity to Magento's module system. It specifies the module name, setup version, and dependencies on other modules.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Vendor_Module" setup_version="1.0.0">
<sequence>
<module name="Magento_Catalog"/>
<module name="Magento_Sales"/>
</sequence>
</module>
</config>
The name attribute matches the module name from registration.php. The setup_version tracks schema and data updates. Magento uses this version to determine which setup scripts to run.
The <sequence> element declares dependencies. Magento loads modules in the correct order based on these declarations. If your module depends on Magento_Catalog, Magento ensures the catalog module is loaded and its database schemas are set up before your module.
etc/config.xml
The config.xml file stores default configuration values. These values populate the admin configuration when the module is first installed.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
<default>
<vendor_module>
<general>
<enabled>1</enabled>
<api_endpoint>https://api.example.com</api_endpoint>
<timeout>30</timeout>
</general>
</vendor_module>
</default>
</config>
Configuration values are accessed in PHP via:
$this->scopeConfig->getValue('vendor_module/general/enabled', \Magento\Store\Model\ScopeInterface::SCOPE_STORE);
etc/di.xml
The di.xml file configures Magento's Dependency Injection container. This is where you define class preferences, plugin configurations, and constructor argument injection. We cover this in the next lesson.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Vendor\Module\Api\ExampleRepositoryInterface"
type="Vendor\Module\Model\ExampleRepository"/>
</config>
etc/routes.xml
The routes.xml file registers frontend or admin routes for your module.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="standard">
<route id="vendor_module" frontName="vendor_module">
<module name="Vendor_Module"/>
</route>
</router>
</config>
The frontName is the URL path segment. If your frontName is vendor_module, URLs look like http://example.com/vendor_module/controller/action/.
For admin routes, place the file in etc/adminhtml/routes.xml with router id admin.
etc/events.xml
Events allow your module to react to actions in other modules. The events.xml file maps event names to Observer classes.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="checkout_cart_add_product_complete">
<observer name="vendor_module_add_to_cart" instance="Vendor\Module\Observer\AddToCart" />
</event>
</config>
Block Directory
The Block/ directory contains block classes that render PHTML templates. Each block class extends Magento\Framework\View\Element\Template and provides data to its template.
<?php
namespace Vendor\Module\Block;
use Magento\Framework\View\Element\Template;
class Greeting extends Template
{
protected $_template = 'Vendor_Module::greeting.phtml';
public function getGreeting(): string
{
return 'Welcome to our custom module!';
}
}
The $_template property defines the default template path. You can also pass a template via layout XML using the template attribute on the <block> element.
Controller Directory
The Controller/ directory contains action classes that handle HTTP requests. Each action class executes a specific task and returns a response.
<?php
namespace Vendor\Module\Controller\Index;
use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Magento\Framework\View\Result\PageFactory;
class Index extends Action
{
protected $resultPageFactory;
public function __construct(Context $context, PageFactory $resultPageFactory)
{
parent::__construct($context);
$this->resultPageFactory = $resultPageFactory;
}
public function execute()
{
return $this->resultPageFactory->create();
}
}
The URL for this controller would be http://example.com/vendor_module/index/index (route vendor_module, controller index, action index).
Model Directory
The Model/ directory contains business logic classes: models, resource models, and collections.
Model class:
<?php
namespace Vendor\Module\Model;
use Magento\Framework\Model\AbstractModel;
class Example extends AbstractModel
{
protected function _construct()
{
$this->_init(\Vendor\Module\Model\ResourceModel\Example::class);
}
}
ResourceModel class:
<?php
namespace Vendor\Module\Model\ResourceModel;
use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
class Example extends AbstractDb
{
protected function _construct()
{
$this->_init('vendor_module_table', 'entity_id');
}
}
Collection class:
<?php
namespace Vendor\Module\Model\ResourceModel\Example;
use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
class Collection extends AbstractCollection
{
protected function _construct()
{
$this->_init(
\Vendor\Module\Model\Example::class,
\Vendor\Module\Model\ResourceModel\Example::class
);
}
}
Setup Directory
The Setup/ directory contains installation and upgrade scripts that create database tables and populate default data.
InstallSchema
Creates database tables when the module is first installed:
<?php
namespace Vendor\Module\Setup;
use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
class InstallSchema implements InstallSchemaInterface
{
public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)
{
$setup->startSetup();
$table = $setup->getConnection()
->newTable($setup->getTable('vendor_module_example'))
->addColumn(
'entity_id',
\Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
null,
['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true],
'Entity ID'
)
->addColumn(
'title',
\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
255,
['nullable' => false],
'Title'
)
->addColumn(
'status',
\Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
null,
['nullable' => false, 'default' => 0],
'Status'
)
->setComment('Vendor Module Example Table');
$setup->getConnection()->createTable($table);
$setup->endSetup();
}
}
InstallData
Inserts default data after the schema is installed:
<?php
namespace Vendor\Module\Setup;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
class InstallData implements InstallDataInterface
{
public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
{
$setup->startSetup();
$setup->getConnection()->insert(
$setup->getTable('vendor_module_example'),
[
'title' => 'Default Item',
'status' => 1
]
);
$setup->endSetup();
}
}
View Directory
The view/ directory contains frontend and adminhtml resources.
view/
frontend/
layout/ # Layout XML files for the storefront
templates/ # PHTML template files
web/ # CSS, JS, images, fonts
requirejs-config.js # RequireJS configuration
email/ # Email template HTML files
adminhtml/
layout/ # Layout XML files for the admin panel
templates/ # Admin panel PHTML templates
web/ # Admin panel static assets
Template Path Convention
When you reference a template in layout XML or block, the path uses the format Vendor_Module::path/to/template.phtml. This maps to view/frontend/templates/path/to/template.phtml in the module or theme.
i18n Directory
The i18n/ directory contains translation CSV files. Each file maps source strings to translated strings for a specific locale.
en_US.csv:
"Submit","Submit"
"Add to Cart","Add to Cart"
de_DE.csv:
"Submit","Absenden"
"Add to Cart","In den Warenkorb"
Api Directory
The Api/ directory contains interfaces that define service contracts. Service contracts are the public API of your module — they define how other modules interact with your code.
<?php
namespace Vendor\Module\Api;
interface ExampleRepositoryInterface
{
public function save(\Vendor\Module\Api\Data\ExampleInterface $example);
public function getById(int $id);
public function delete(\Vendor\Module\Api\Data\ExampleInterface $example);
}
Enabling a Module
Once your module's registration.php and etc/module.xml are in place, enable it with these commands:
# Enable the module
bin/magento module:enable Vendor_Module
# Run setup upgrade to execute install scripts
bin/magento setup:upgrade
# Compile dependency injection configuration
bin/magento setup:di:compile
# Deploy static content for production mode
bin/magento setup:static-content:deploy -f
# Clear all caches
bin/magento cache:clean
bin/magento cache:flush
Checking Module Status
# List all enabled and disabled modules
bin/magento module:status
# Check if a specific module is enabled
bin/magento module:status Vendor_Module
Disabling a Module
bin/magento module:disable Vendor_Module
bin/magento setup:upgrade
bin/magento cache:flush
Module disabling is rarely needed for custom modules. It is more common for third-party extensions that conflict with each other.
Module Dependencies
Modules declare dependencies in module.xml using the <sequence> element. Magento processes modules in sequence order:
<module name="Vendor_Module" setup_version="1.0.0">
<sequence>
<module name="Magento_Sales"/>
<module name="Magento_Customer"/>
</sequence>
</module>
This ensures that Magento_Sales and Magento_Customer are loaded and their schemas are installed before Vendor_Module runs its setup scripts. If your module uses sales entities or customer data, declare the dependency explicitly.
Common Mistakes
Mismatched module names. The module name in
registration.php,module.xml, and the directory structure must all match exactly.Vendor_Modulein registration.php butvendor_modulein module.xml causes a fatal error. The format is alwaysVendorName_ModuleNamewith capital first letters and an underscore separator.Missing setup scripts after schema changes. When you add a new database table or modify an existing one, you need to either create an
UpgradeSchemascript or increment thesetup_versioninmodule.xml. Without this, Magento does not know that schema changes are pending.Forgetting
setup:upgradeafter enabling. Enabling a module only registers it. You must runbin/magento setup:upgradeto execute install scripts that create database tables and insert default data. Skipping this step results in errors when the module tries to access missing tables.Registering classes with wrong namespace. The PHP namespace must match the directory structure.
Vendor\Module\Block\Greetingrequires the fileapp/code/Vendor/Module/Block/Greeting.php. A mismatch causes a "Class not found" error that is hard to debug.Putting everything in one module. A module should have a single responsibility. Do not create a "Utils" module that handles loyalty points, SEO, and shipping all together. Split functionality into focused modules. This makes upgrades, testing, and debugging much easier.
Practice Questions
What are the two required files for a Magento module? Answer:
registration.php(registers the module withComponentRegistrar) andetc/module.xml(declares the module name, setup version, and dependencies). Without these two files, Magento does not recognize the module.What is the purpose of the
setup_versionattribute in module.xml? Answer: Thesetup_versiontracks the current schema and data version of the module. Magento compares this version against the database record to determine which setup scripts to execute. Incrementing the version triggers upgrade scripts defined inSetup/UpgradeSchema.phporSetup/UpgradeData.php.What does the
<sequence>element in module.xml do? Answer: The<sequence>element declares dependencies on other modules. It tells Magento to load and initialize the specified modules before your module. This ensures that database tables, configuration, and classes from dependent modules are available when your module runs.Challenge: Create a complete module called
Vendor_ProductAlertthat adds a custom database tablevendor_productalert_notificationwith columns forentity_id,product_id,customer_id,created_at, andnotified_at. Include theregistration.php,module.xml,InstallSchema.php, and a block class with a corresponding template. Enable the module and verify the table exists in the database usingSHOW TABLES;.
FAQ
Mini Project
Your task: Create the complete module skeleton for a custom "Customer Referral" module.
- Create the directory
app/code/Vendor/CustomerReferral/. - Create
registration.phpwithComponentRegistrar::register. - Create
etc/module.xmlwith dependencies onMagento_CustomerandMagento_Sales. - Create
etc/config.xmlwith a default configuration valuecustomer_referral/general/reward_points = 100. - Create
etc/di.xmlwith a preference for a Repository interface. - Create
Block/Referral.phpthat extends Template. - Create
Model/Referral.php,Model/ResourceModel/Referral.php, andModel/ResourceModel/Referral/Collection.php. - Create
Setup/InstallSchema.phpwith avendor_customerreferral_referraltable. - Enable the module and verify it appears in
bin/magento module:status.
This exercise covers the most common module structure that you will use in almost every Magento project. A well-structured module is easier to maintain, debug, and share with other developers.
What's Next
Now that you understand module structure, the next step is learning how Magento wires classes together:
Continue to Lesson 26: Dependency Injection — di.xml, preferences, type configuration, and virtual types.
Related lessons:
- Magento Plugins — Extend any class without modifying it
- Magento Observers and Events — React to actions across modules
- PHP Namespaces and Autoloading — Understand how Magento loads classes
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro