Reading more and more about Perl, I'm having doubts about how I organized my modules on my current project.
I have a main namespace - let's call it "MyProject".
In this project, the base data are graphs in which each object has a class. objects are linked with relations. Both objects and relations can have attributes.
There is a part of the project where I use a model to validate these graphs. So I have a Model class that is composed of objects of class Class, Relation and Attribute.
Classes Class, Relation and Attribute are used by class Model only, so it made sense to me to organize the modules as follows:
But I'm starting to think that it will make sense to me only if I ever dare to relase parts of my project in CPAN. I think people will believe that Class, Relation and Attribute are inheriting Model and not composing it.
So: shall I reorganize my modules this way:
Or maybe indicate that Class, Relation and Attribute are parts of Model by appending their names?
My question: What is the currently considered a best practices for module organization and naming when it comes to composition?
Cross-posted at Perlmonks
Your concern is correct. Typically, packages are named by either inclusion or inheritance. I'm going to make up a more realistic example. Let's say we are building an online shop.
Inclusion
You first pick a namespace for your project. In the business world, you often see namespaces with the company name first, to distinguish proprietary modules from CPAN ones. So the project's namespace could be:
OurCompany::Shop
Then probably the main class or module for the application is called the same, so we have a
OurCompany/Shop.pm
We will have a bunch of things that we need to make an online shop. If our project is MCV, there are controllers and models and stuff like that. So we might have these things:
OurCompany::Shop::Controller::ProductSearch
OurCompany::Shop::Controller::Cart
OurCompany::Shop::Controller::Checkout
OurCompany::Shop::Model::Database
All of those map to modules direcly.
OurCompany/Shop/Controller/ProductSearch.pm
OurCompany/Shop/Controller/Cart.pm
OurCompany/Shop/Controller/Checkout.pm
OurCompany/Shop/Model/Database.pm
But there is no OurCompany::Controller
as a base class. That name is just a namespace to sort things into.
Then there are some things that are just there, and get used by OurCompany::Shop, like the Session engine.
OurCompany::Shop::Session
Those go on the first level after the project, unless they are very specific.
Now of course there is some kind of engine behind the session system. Let's say we are fancy and use Redis for our sessions. If we implement the communication ourselves (which we wouldn't because CPAN has done that already), we would stick that implementation into
OurCompany::Shop::Session::Engine::Redis
The only thing that uses this module is OurCompany::Shop::Session under the hood. The main application doesn't even know what engine is used. Maybe you don't have Redis on your development machine, so you are using plain files.
OurCompany::Shop::Session::Engine::File
Both of them are there, they belong to ::Session, but they don't get used by any other part of the system, so we file them away where they belong to.
Inheritance
We will also have Products1. The base product class could be this.
OurCompany::Shop::Product
And there is a file for it.
OurCompany/Shop/Product.pm
Just that this base product never gets used directly by the shop, other than checking that certain things have to have ::Product in their inheritance tree (that's an isa
check). So this is different from ::Session, which gets used directly.
But of course we sell different things, and they have different properties. All of them have prices, but shoes have sizes and hard drives have a capacity. So we create subclasses.
OurCompany::Shop::Product::Shoe
OurCompany::Shop::Product::HardDrive
And those have their own files.
OurCompany/Shop/Product/Shoe.pm
OurCompany/Shop/Product/HardDrive.pm
We might also distinguish between a mechanical HardDrive and an SSD, so we make an ::SSD subclass.
OurCompany::Shop::Product::HardDrive::SSD
OurCompany/Shop/Product/HardDrive/SSD.pm
So basically things are put in the same namespace if they belong to each other. Here's a tree view of our lib.
.
└── OurCompany
├── Shop
│ ├── Controller
│ │ ├── Cart.pm
│ │ ├── Checkout.pm
│ │ └── ProductSearch.pm
│ ├── Model
│ │ └── Database.pm
│ ├── Product
│ │ ├── HardDrive
│ │ │ └── SSD.pm
│ │ ├── HardDrive.pm
│ │ └── Shoe.pm
| ├── Product.pm
│ └── Session.pm
│ │ └── Engine.pm
│ │ ├── File.pm
│ │ └── Redis.pm
└── Shop.pm
To sum up:
Never append things together like MyProject::AB to indicate that it belongs to MyProject::A. Always use namespace separators to organize your namespaces. Consider this, which looks just plain wrong:
OurCompany::ShopProductShoe
1) It might be that we also have a backoffice application that also uses the same product classes. In that case, we might have them as OurCompany::Product, and they get used by two different projects, OurCompany::Shop and OurCompany::BackOffice.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With