Henrique Barcelos
Henrique Barcelos

Reputation: 7900

Modular design and intermodule references

I'm not so sure the title is a good match for this question I want to put on the table.

I'm planning to create a web MVC framework as my graduation dissertation and in a previous conversation with my advisor trying to define some achivements, he convinced me that I should choose a modular design in this project.

I already had some things developed by then and stopped for a while to analyze how much modular it would be and I couldn't really do it because I don't know the real meaning of "modular".

Some things are not very cleary for me, like for example, just referencing another module blows up the modularity of my system?

Let's say I have a Database Access module and it OPTIONALY can use a Cache module for storing results of complex queries. As anyone can see, I at least will have a naming dependency for the cache module.

In my conception of "modular design", I can distribute each component separately and make it interact with others developed by other people. In this case I showed, if someone wants to use my Database Access module, they will have to take the Cache as well, even if he will not use it, just for referencing/naming purposes.

And so, I was wondering if this is really a modular design yet.

I came up with an alternative that is something like creating each component singly, without don't even knowing about the existance of other components that are not absolutely required for its functioning. To extend functionalities, I could create some structure based on Decorators and Adapters.

To clarify things a little bit, here is an example (in PHP):

Before

interface Cache {
    public function isValid();
    public function setValue();
    public function getValue();
}

interface CacheManager {
    public function get($name);
    public function put($name, $value);
}

// Some concrete implementations...

interface DbAccessInterface {
    public doComplexOperation();
}

class DbAccess implements DbAccessInterface {
    private $cacheManager;

    public function __construct(..., CacheManager $cacheManager = null) {
        // ...
        $this->cacheManager = $cacheManager;
    }

    public function doComplexOperation() {
        if ($this->cacheManager !== null) {
            // return from cache if valid
        } 
        // complex operation
    }
}

After

interface Cache {
    public function isValid();
    public function setValue();
    public function getValue();
}

interface CacheManager {
    public function get($name);
    public function put($name, $value);
}

// Some concrete implementations...

interface DbAccessInterface {
    public function doComplexOperation();
}

class DbAccess implements DbAccessInterface {
    public function __construct(...) {
        // ...
    }

    public function doComplexQuery() {
        // complex operation
    }
}

// And now the integration module

class CachedDbAcess implements DbAccessInterface {
    private $dbAccess;
    private $cacheManager;

    public function __construct(DbAccessInterface $dbAccess, CacheManager $cacheManager) {
        $this->dbAccess = $dbAccess;
        $this->cacheManager = $cacheManager;
    }

    public function doComplexOperation() {
        $cache = $this->cacheManager->get("Foo")
        if($cache->isValid()) {
            return $cache->getValue();
        }
        // Do complex operation...
    }
}

Now my question is: Is this the best solution? I should do this for all the modules that do not have as a requirement work together, but can be more efficient doing so?

Anyone would do it in a different way?

I have some more further questions involving this, but I don't know if this is an acceptable question for stackoverflow.

P.S.: English is not my first language, maybe some parts can get a little bit confuse

Upvotes: 0

Views: 432

Answers (3)

Fendy
Fendy

Reputation: 4643

I came up with an alternative that is something like creating each component singly, without don't even knowing about the existance of other components that are not absolutely required for its functioning

It is great if you came up with this idea by yourself. The statement itself, is a key to modular programming.

Plugin architecture is the best in terms of extensibility, but imho it is hard to maintenance especially in intra application. And depending the complexity of plugin architecture, it can make your code more complex by adding plugin logics, etc.

Thus, for intra modular design, I choose the N-Tier, interface based architecture. Basically, the architecture relays on those tiers:

  1. Domain / Entity
  2. Interface [Depend on 1]
  3. Services [Depend on 1 and 2]
  4. Repository / DAL [Depend on 1 and 2]
  5. Presentation Layer [Depend on 1,2,3,4]

Unfortunately, I don't think this is achieveable neatly in php projects as it need separated project / dll references in each tier. However, following the architecture can help to modularize the application.

For each modules, we need to do interface-based design. It can help to enhance the modularity of your code, because you can change the implementation later, but still keep the consumer the same.

I have provided an answer similiar to this interface-based design, at this stackoverflow question.

Lastly but not least, if you want to make your application modular to the UI, you can do Service Oriented Architecture. This is simply make your application as bunch of services, and then make the UI to consume the service. This design can help to separate your UI with your logic. You can later use different UI such as desktop app, but still use the same logic. Unfortunately, I don't have any reliable source for SOA.

EDIT:

I misunderstood the question. This is my point of view about modular framework. Unfortunately, I don't know much about Zend so I will give examples in C#:

  • It consist of modules, from the smallest to larger modules. Example in C# is you can using the Windows Form (larger) at your application, and also the Graphic (smaller) class to draw custom shapes in the screen.
  • It is extensible, or replaceable without making change to base class. In C# you can assign FormLoad event (extensible) to the Form class, inherit the Form or List class (extensible) or overridding form draw method to create a custom window graphic (replaceable).
  • (optional) it is easy to use. In normal DI interface design, we usually inject smaller modules into a larger (high level) module. This will require an IOC container. Refer to my question for detail.
  • Easy to configure, and does not involve any magical logic such as Service Locator Pattern. Search Service Locator is an Anti Pattern in google.

I don't know much about Zend, however I guess that the modularity in Zend can means that it can be extended without changing the core (replacing the code) inside framework.

If you said that:

if someone wants to use my Database Access module, they will have to take the Cache as well, even if he will not use it, just for referencing/naming purposes.

Then it is not modular. It is integrated, means that your Database Access module will not work without Cache. In reference of C# components, it choose to provide List<T> and BindingList<T> to provide different functionality. In your case, imho it is better to provide CachedDataAccess and DataAccess.

Upvotes: 1

OpenMinded
OpenMinded

Reputation: 486

Just referencing another module blows up the modularity of my system?

Not necessarily. It's a dependency. Having a dependencies is perfectly normal. Without dependencies modules can't interact with each other (unless you're doing such interaction indirectly which in general is a bad practice since it hides dependencies and complicates the code). Modular desing implies managing of dependencies, not removing them.

One tool - is using interfaces. Referencing module via interface makes a so called soft dependency. Such module can accept any implementation of an interface as a dependency so it is more independant and as a result - more maintainable.

The other tool - designing modules (and their interfaces) that have only single responcibility. This also makes them more granular, independant and maintainable.

But there is a line which you should not cross - blindly applying these tools may leed to a too modular and too generic desing. Making things too granular makes the whole system more complex. You should not solve universe problems, making generic modules, that all developers can use (unless it is your goal). First of all your system should solve your domain tasks and make things generic enough, but not more than that.

Upvotes: 1

ultima_rat0
ultima_rat0

Reputation: 290

Some resources (not theoretical):

Upvotes: 1

Related Questions