PHP

Optimize Magento in the Cloud

17.07.2012

POSTED IN PHP, PHP 5 | Comments Off TAGS : ,

In this follow-up tutorial, we’ll optimize Magento, add a Redis cache, SSL and DNS alias, then scale the application for benchmarking and production. The following assumes you’ve already used the first tutorial to launch a basic Magento cloud application on Pagoda Box. Ready to optimize?


Requirements

You can download the finished code for this tutorial, or install Magento from a Quickstart to test a working site.

Fair Warning: Pagoda Box is not traditional hosting. This tutorial not only optimizes Magento, it lays the groundwork for a progressive continuous integration workflow.


Step 1: Enable eAccelerator, XCache or APC Cache

Enabling a Bytecode Cache will optimize performance, especially with large codebases such as Magento. You could use eAccelerator or XCache bytecode caching, but Magento recommends APC. To enable APC, simply add it to the list of php extensions in the Boxfile as follows:

web1:
  php_extensions:
    - apc

Your new Boxfile should look like this:

web1:
  name: mag-app
  shared_writable_dirs:
    - media
    - var
  php_version: 5.3.8
  php_extensions:
    - pdo_mysql
    - mysql
    - simplexml
    - mcrypt
    - hash
    - gd
    - dom
    - iconv
    - curl
    - soap
    - apc
  after_build:
  - "mv pagoda/local.xml app/etc/local.xml"
cron:
    - "*/15 * * * *": "curl -s -o /dev/null http://magento.pagodabox.com/cron.php"
db1:
  name: mag-db
  type: mysql

Step 2: Create a Redis Component

By default, Magento sessions on Pagoda Box are located in writable storage, and are accessible to all your web instances. This tutorial moves sessions to Redis, which is an “advanced key-value store”. Redis provides extremely powerful in-memory caching, coupled with persistent data for far better read, write, and session performance.

Create a Redis Cache from the Boxfile

First, enable the Redis extension by adding - redis to the extensions list in the Boxfile, just as we did for APC. This allows your PHP components to connect with Redis. Also add a Redis cache component to your Boxfile as follows:

cache1:
  name: mag-cache
  type: redis

Your updated Boxfile should look like this:

web1:
  name: mag-app
  shared_writable_dirs:
    - media
    - var
  php_version: 5.3.8
  php_extensions:
    - pdo_mysql
    - mysql
    - simplexml
    - mcrypt
    - hash
    - gd
    - dom
    - iconv
    - curl
    - soap
    - apc
    - redis
  after_build:
  - "mv pagoda/local.xml app/etc/local.xml"
cron:
    - "*/15 * * * *": "curl -s -o /dev/null http://magento.pagodabox.com/cron.php"
db1:
  name: mag-db
  type: mysql
cache1:
  name: mag-cache
  type: redis
  

When a Redis component is included in the Boxfile, a Redis cache is automatically added to your infrastructure when you deploy updates.

Alternate: Create a Redis Cache from the Dashboard

You can also create a Redis cache from the Pagoda Box Dashboard. The Dashboard is also where you scale the Redis component.

First, click “Add Cache” in the Dashboard.

…then choose Redis.

Once your new cache component has been deployed, click it in the Dashboard, then choose your preferred amount of RAM as follows:

Data is persistant with Redis, so you can resize the cache without losing sessions or caching data held in memory, unlike Memcache.


Step 3: Configure Magento for Redis Sessions and Cache

This tutorial shares a single Redis component for Magento sessions and caching. If your site receives enough traffic, creating a separate Redis component for both sessions and caching can provide an added measure of performance.

Redis Sessions

Configure Magento sessions to use Redis.

If you ever need to override Magento default files (like we will here), do not modify Magento’s core files. Override files should always reside in Magento’s Local Directory to protect functionality and future upgrades.

  • Edit the session handler section of your local.xml file to use Redis, and set the session save path as follows:

    <session_save><![CDATA[redis]]></session_save>
    <session_save_path><![CDATA[tcp://tunnel.pagodabox.com:6379]]></session_save_path>
    
  • Next, add Redis support to Magento by overriding a core Magento file. Copy the file:

    app/code/core/Mage/Core/Model/Session/Abstract/Varien.php
    

    Then create your own local version at:

    app/code/local/Mage/Core/Model/Session/Abstract/Varien.php
    

    In the new file, around line 62, add the following:

    case 'redis':
                    ini_set('session.save_handler', 'redis');
                    session_save_path($this->getSessionSavePath());
                    break;
    

Redis Cache

Now, configure Magento cache to use Redis, as well.

  • Add the Credis library and Cm Module to enable Redis as a Magento cache. The module consists of one file, which you can download here. Although the Cm module author recommends installing via modman, modman doesn’t correctly register all submodules with Pagoda Box. Instead, save the file as: app/code/community/Cm/Cache/Backend/Redis.php.

    Add the Credis library as a Git submodule to your project:

        $ cd your_site_dir
        $ git submodule add git://github.com/colinmollenhour/credis.git lib/Credis
        
  • Once again, edit the local.xml file. Change the cache delcaration and it’s contents as follows:

            <cache>
              <backend>Cm_Cache_Backend_Redis</backend>
              <backend_options>
                <server>tunnel.pagodabox.com</server>
                <port>6379</port>
                <database>0</database>
                <force_standalone>0</force_standalone>
                <automatic_cleaning_factor>0</automatic_cleaning_factor>
                <compress_data>1</compress_data>
                <compress_tags>1</compress_tags>
                <compress_threshold>20480</compress_threshold>
                <compression_lib>gzip</compression_lib>
              </backend_options>
            </cache>
    
  • Set far future expires to drastically reduce the number of requests per visitor as they browse your site. Do this after finishing frontend theme work. Adding the following static_expire directive to the web1 section of your Boxfile will expire static asset caches after 1 year.

    web1:
      static_expire: 31536000
    
  • Add the updated files to git, commit and push to Pagoda Box.

    	$ git add .
    	$ git commit -m 'installed and configured redis'
    	$ git push pagoda --all
    
  • After deploying, clear the Magento system cache by logging into your Magento admin and going to System -> Cache Management, then clicking ‘Flush Magento Cache’.

NOTE: Avoid JS & CSS in Writable Storage

Magento has a few optimization utilities that merge javascript and css files, then place them in Pagoda Box’s shared writable storage. Most of the time, that’s a non-issue, but at high traffic volume, Pagoda Box can access files from the distributed web instances much faster than files in shared writable storage. For high traffic performance, keeping javascript and css in the repo outperforms minification.

To turn file merging off, log into your Magento admin panel, then navigate to: System > Configuration > Developer

Choose “No” under JavaScript Settings and CSS Settings as follows:


Step 4: Verify Redis is Working

Now that Magento is configured for Redis, verify that everything is working. Follow this Redis guide to establish a secure tunnel, then use the terminal to access Redis on Pagoda Box. Once connected, run the following command to display a list of all stored keys.

$ keys *

Assuming you’ve browsed the udpated site, you should see a list of assorted keys. If you see session keys only, cache keys only, or if you don’t see any keys at all, review your configuration settings and ensure you’ve cleared the Magento cache.

For a full list of useful commands when using Redis, see their official documentation.


Step 5: Adding DNS / SSL

Piggyback SSL is free and automatic for every app on Pagoda Box at their app’s subdomain (appname.pagodabox.com). While it’s possible to benchmark using Piggyback SSL, we’ll enable Third-Party SSL for purposes of the tutorial. Wildcard Certs and Self-signed SSL are detailed in this
“>DNS / SSL guide
.

Add SSL

Begin by clicking the “Add an SSL Certificate” button under the DNS / SSL Tab in the Pagoda Box dashboard.

Next, choose whether you’re creating a certificate, or using an existing SSL certifcate, as we are here. Assuming you’ve got an existing certificate, click “Transfer Existing”.

Finally, paste the relevant information into the appropriate field and activate. Your SSL Certificate is now saved with your application.

Add A DNS Alias

After you’ve entered an SSL Certificate, it will appear in your list of available SSL Certificates. If you haven’t already, add your custom domain as a DNS Alias to your application. Click “Add New Alias” and enter your registered domain as the alias. Pagoda Box will assign a shared IP address by default, but this will change when you associate an SSL certificate with the DNS Alias.

Finally, associate your SSL certificate with your DNS alias by selecting it from the SSL dropdown list next to your DNS Alias, then click “Save”. With the SSL certificate now tied to your DNS Alias, your alias gets a unique, dedicated IP address.

Once you associate an SSL Certificate with a DNS alias, that alias will get a dedicated IP address. Make sure to verify that your A-Record matches the IP provided after you add, delete or modify your SSL Certificates.


Step 6: Benchmark with Blitz.io

We’ve used Blitz.io to benchmark a few Magento applications. It tests both http response and database writes using variable lists URLs and Cookies.

To start, create a free account on Blitz.io. Once you’ve registered, look for the “START!” button at the bottom of your welcome page.

Click “Start”, and Blitz will provide a series of simple tutorials that explain the variables you’ll use in a Query URL to test your application. For our purposes, your base Query URL should look something like this:

-p 1-24:60 -r california -T 4000 -v:cookie list[d1b1530ff8694817e1f11f55e0a4a120,805d9a11f3b9f8ea35ee2db1d9aac3fc] -v:urls list[index.php,furniture.html,ottoman.html,about-magento-demo-store,checkout/cart/add?product=51] -b "my-cookie=#{cookie}Ó http://magento-demo.pagodabox.com/#{urls}

Specifically, that query will gradually test 1-24 concurrent visitors (can test to 250 with Free account) over 60 seconds, from California, timing out at 4 seconds. The query also sets a list of cookies and URLs as variables, plus the domain we’re testing. We’ve included a Home page, CMS page, Catalog page, Product page and Checkout page to test.

Using Cookie variables and a checkout/cart/add?product URL lets us test the DB writes. Obtain a list of valid cookies by navigating your site in Chrome > Inspect Element > Resources > Cookies > [yoursite] > frontend. Copy the key from the Value column, as indicated below:

Replace the cookies, URLs and Domain from the URL query above with the appropriate elements from your site, then enter the new query on Blitz.io, and click “Run”.

The first time you “Rush” your app, Blitz will display an Authorization Error and ask you to prove ownership by adding a specific URL to your app. Follow the onscreen Blitz instructions for creating a unique authorization file on your local computer, then push changes to Pagoda Box.

	$ git add .
	$ git commit -m "blitz auth file"
	$ git push pagoda --all

Confirm the new file is in place by visiting the URL in a browser. You should see “42″. Note that the “root of your www directory” is the root of your project unless you have configured document_root in your Boxfile to be another directory.

Once everything is in place, you’re free to adjust Blitz variables and test. For example, changing 1-24 to 60-60 will change concurrency from a graduated increase to 24 concurrency to 60 sustained concurrent visitors. Isolating URLs in the variable list allows you to identify performance bottlenecks by isolating various page types. You can even test DB write capacity by using the checkout/cart/add? url.

Of course, benchmarking is simply an indicator to help scale the application. Make sure to scale to various levels as you benchmark, noting the change in results (see Scaling Magento video). Blitz.io should help identify application bottlenecks and determine the appropriate scale for your expected traffic.


Step 7: Scaling on Pagoda Box

There has been quite a bit of initial prep work to get to this stage, but this is where it all comes together. Continuous integration, ongoing management, and scalability are now much simpler. As you benchmark, keep in mind that charges are hourly. Scaling to test briefly will cost only a few dollars.

Scaling Web Instances

To scale your web application for more traffic, open your Web Component from the Pagoda Box dashboard as follows:

Then, click and drag the slider to the desired number of instances, and click “Save”. Pagoda Box will deploy Magento from your repository to new instances. After all instances are built and verified, the routing layer will redirect traffic to the new instances, and decommission the old without any downtime (again, see Scaling Magento video).

Note: As you’re using it, the slider appears to max out at 25 instances, but will reset for greater scaling after the upper limit has been saved.

Scaling a Database : Cloud (Cache Components are identical)

To scale your cloud database, open your Databsase Component from the Pagoda Box dashboard as follows:

Notice the Red Face is unhappy because RAM usage is too high. To increase RAM, click the green “Change” arrow, select your desired amount of RAM, and click “Save”.

Pagoda Box will create a larger database, migrate data, sync via master-master replication, then point your application to the new database. Your site is live throughout the migration.

Scaling a Database : Private

To scale your Private database, open your Databsase Component from the Pagoda Box dashboard as follows:

Select the resources you’d like for your database, determine if you’d like replication, then click “Save”.

Pagoda Box will create a larger database, migrate data, sync via master-master replication, then point your application to the new database. Your site is live throughout the migration.

Keep in mind that ordering a Private database may take more than 20 minutes, as a server is provisioned to your specifications.

Note: currently, scaling from a Cloud database to a Private database requires some downtime as you manually migrate data. Scaling from Cloud to Cloud, or Private to Private is always automatic.


Summary

That’s it! Your Magento application is now fully scalable in an instant, and updates are easily deployed with $ git push pagoda --all. Enjoy!



Let’s admit it: the PHP community has lagged a bit, when it comes to advocating test-driven development. We know we should, but, even to this day, a sizable portion of the community does not. In this new series of videos and tutorials, created by the Nettuts+ team, we’re hoping to change that.

Trust me: it’s not as tough as you think.


Lesson 1 – Setup

In this series, we’ll be using Composer, a dependency manager, to install PHPUnit (or at least, the unofficial version for now). To ensure that we’ve installed it properly, we’ll also create a quick calculator test and class.

Choose 720p for the clearest picture.

 

Enhanced by Zemanta

Domain-Driven Design

11.07.2012

POSTED IN 3d, Design, PHP, PHP 5, Tutorials | Comments Off TAGS : ,

In my country, you won’t make it through school without reading how Goethe’s Faust complains, I’ve studied now Philosophy – And Jurisprudence, Medicine, – And even, alas! Theology – All through and through with ardour keen! – Here now I stand, poor fool.

Sadly, none of his efforts and studies helped the doctor to perceive whatever holds the world together in its inmost folds.

And here we are in IT: We’ve studied languages and frameworks, libraries and even – alas – the IE! All through and through with ardour keen. But how many times did we focus on whatever holds the application together in its inmost folds? Today’s topic is the business domain.


Business Logic and Software Design

Business logic is sometimes considered to be unique, and it is by definition! If the business logic of an application wouldn’t be unique, there’d be no need to write an application, as there’s already an existing solution (with the exception of when an application exists but is not available). Hence, many developers see themself as pioneers, to boldly go where no man has gone before. Romantics aside, while the business logic itself may be unique to a noteworthy degree, the techniques to implement it are not. That’s why smart process models like Rational Unified Process or Scrum along with techniques like iterative and incremental development cycles were invited. Talented software architects have elaborated approaches for software design as well; among them Eric Evans who coined the term Domain Driven Design in his book with the same title.

Developers go boldly, where no man has gone before.

I’ll give an overview on how Domain Driven Design can influence the consulting process, as well as its basic concepts for designing a domain model. Finally, we will discuss the infrastructure requirements that are needed to implement a domain with ease.

Engineering Requirements

Let’s say you are a software architect on a non-trivial application with a non-trivial domain, like the core engine of a large logistic company. Many people are joining the planning talks, among them project managers, account managers, marketing, consultants and so on. Not everyone is needed to get the job done (I won’t share my opinions on to whom this applies), but two people will play a crucial role during the process of requirements engineering: you, the architect, and the domain expert.

A software architect (at least in business context) should have a very good abstract understanding on how processes work, how they are designed and optimized.

That’s true because business applications are mainly about designing efficient and beautiful digital equivalents of business processes. A domain expert should have an in-depth knowledge about a specific set of processes, namely the processes that are taking place in the logistic company and that should be reflected by the application. I found that business consultants, sales manager and marketing experts make a few good and valuable points along the way, but as long as you don’t have someone in the team who got his hands dirty in years of experience, the project will fail. For example, your domain expert should know the width of the loading ramp at the depot and if there’s enough space to install a barcode scanner.

So it’s you, specialized in digital business processes, software design and [fill in your favourite tools here], and an expert on logistics with knowledge on the company’s clients, employees and the day-to-day routine. Chances are, you’ll talk at cross-purposes. Domain Driven Design suggests some strategies that can form a powerful technical consulting service. Here’s mine:

  • Create an ubiquitos language
  • Build a glossary of keywords
  • Shift from a process oriented view to a domain centered approach.
  • Build a visual model as the foundation of your business logic.

Sounds like fun! Let’s dive into the details.

In every industry, every group of experts has its own terminology. It’s refined in every company and enriched with the companies special terms and product names. Think of IT: when people like us meet for serious geek talk, who else would understand a word? The same is true for your domain, and the first thing to do is to define a set of terms. Walk through the entire set of processes that the software should reflect and listen closely how the domain expert describes it. Any domain specific terms should be defined in a way that dictionaries do. You should be aware of words that sound familiar but are not in the given context. Some companies have never done that job before, even if it’s valuable for other areas.

Make a dedicated glossary of your ubiquitous terms, be sure that it gets approved by the client, and charge for the consulting process! A glossary may look like this:

An extract from a glossary.

Notice how a well defined glossary already sets dependencies and associations. Like the order, that hosts multiple items. You will surely have classes for those in your business logic! Your Order class will presumably have a method like getItems(). Without taking programming techniques into account, a glossary can set the ground work for your domain model! Along with that, you are building a language that is used throughout the entire project: in mails, in meetings, and definitely in code! Your code should reflect the domain; hence, it must be defined in the glossary. Here’s a rule of thumb: whenever you are creating a class that is not named after an entry in your glossary, your ubiquitous language may not be defined sufficiently yet!

Under the cover of darkness we have shifted the view on the requirements! Normally, a client describes what the to-be-written-software should do. A typical description might be, “We need a way to add notes to a client and print them out.” This is a good starting point, but it’s not focusing on the business domain. It introduces some kind of user interface, print functionality, and even more. You’ll surely need that in your application, but it’s not part of the domain. Domain Driven Design focuses on the modeling of the true purpose of an application: the business domain.

Everything else should arise from there, once the domain is done. The switch is the following: don’t implement processes, and build a pure domain that reflects the clients needs in objects. A way of visualizing the upper client description would be like this (getters and setters are only added, when needed for the understanding):

A simple diagram that reflects the required domain.

Now we have a set of classes and associations that do nothing but reflect the definitions from our glossary. Is this model capable of performing the needed tasks? Sure! You’ll need a PrinterService and a user interface somewhere in your app, but they just need to grab some data from the domain. They are not necessary right now, and its implementation will not be decisive for the outcome.

The philosophy of DDD is based on the assumption, that a carefully designed domain layer can perform all needed processes with ease. A domain model is scalable, as it’s not built to satisfy a given task, it’s built to reflect a business concept. It’s interchangeable, as it’s not bound to any specific software—not even to a user interface. You can use the very same model in the barcode scanner on the loading ramp at the depot! As we’ll see in the next chapter, it’s not even bound to other components that are building your application.


The Domain Model

In one of my recent articles, I wrote about applying the KISS principle.

In one of my recent articles, I wrote about applying the KISS principle: most systems work best if they are kept simple rather than made complex. Well, when it comes to implementing a domain based on the philosophy of DDD, you can encounter a rather radical approach in the modern world of frameworks, patterns, and disciplines; such as, implement an ordinary object in just the plain language of your choice. No framework dependencies, no library conventions, no traces of any API, no fancy names. Just a plain old object (since a concept is not taken serious without a fancy name in the world of Java, they got one over there).

Entities vs. Value Objects

When we want to reflect the domain model, a crucial point is to define its state. In object oriented programming, the state of an object is defined by the state of its properties. Likewise, the state of the domain model is defined by the state of its objects. Hence, we must have a way to clearly define the state of objects. If we couldn’t do that, we would fail at easy use cases like, “How many orders are there?” because the answer always requires knowledge about the state of all Order objects in a domain and a way to identify and distinguish them. DDD defines two types of objects: Entities and value objects.

An entity is a familiar concept, if you are familiar with relational databases.

Tables in a relational database usually have a unique identifier that distinguishes one row from another. The same is true for entities. An entity must have a clear identifier that is unique in the entire system. For an order, this could be a property of the type uint, named orderNumber. Of course, you’d look into your glossary, where the correct term should be defined.

An entity stays the same when some properties change. For example you can add or remove items from an order, but it would be the same order. What happens, when you are changing the orderNumber? Well, from the POV of your domain, one order is deleted while another one is created.

A value object is simple container for information. It is immutable once it is created. Changing one property means that you’d change the value object. A value object is defined by all of its properties; it does not need a unique identifier. The whole object is one. An example of a value object would be an OrderAddress, as it’s defined by the name, address and city of the recipient. If you’d change one property, for example the city, the OrderAddress would change completely.

If you change one property of a color, it would be a different one.

Dividing objects into value objects and entities is important to define the state of your domain – as this is the ground work to identify components. But it’s as well important to define them to have a scalable a maintainable domain. Entities are the representation of real world objects like Persons, Orders or Items. Value Objects are containers for information like colors or addresses and they are reusable and shareable among entities or even your entire system. Defining them may take some practice, as it’s depending on the use case whether you have a value object or an entity.

Associations, Aggregates, and Repositories

When we have a look back on the abstract of our glossary, we can see connections and dependencies between our objects in the domain layer. In DDD, this is called associations and it’s the model of interactions that are taking place.

For example, items are part of the order. If we’d process against a relational database, this would be a one-to-many relationship (or 1:n). If every order would have exactly one OrderAddress, it would be a one-to-one relationship. Since we do not care about relational databases and do only care about finishing the domain, the relationship can be easily expressed with two methods in the Order class: getItems() and getOrderAddress(). Note, that the first is plural (as there are many items) and the second is singular. If you’d have a many-to-many relationship, you would give both classes a getter method. Of course, you need setters, too—I’ve omitted them to keep the examples lightweight.

Items and the order address can be seen as children of the order.

In DDD we try to avoid many-to-many relationships, as they tend to add complexity to the domain. Technically it means, that two objects have to be kept in sync during their lifecycle, and keeping things in sync can lead to violation of the DRY principle. That’s why the model refinement process should strive for simplicity. In many times, an association is stronger in one direction than the other, and it’s a good idea to redesign the structure to a one-to-many relationship. Check if the association is relevant for the business logic of your application. If it’s just occurring in non-core and rare use cases, you may want to look for another way to receive the needed information.

Associations build a tree of objects and you should end up with an association construct where every object can be retrieved through getter methods. This is a parent-child construct that ultimately leads to one root object. This is called aggregation in DDD. A good design ultimately leads to one aggregate that is capable of reflecting the entire domain. At the moment we have only looked at a small part of our glossary, but it seems that a client is our root aggregate:

The client object is the parent of all domain members.

Aggregates are an important part, as DDD tries to isolate the domain from the surrounding application. If we like to have information about a client, we ask for a root aggregate and can traverse through its children to access information through a clear interface of getters and setters.

DDD is like sales, it provides one face to the customer, the aggregate to the surrounding system. Hence, it gives access to a structured set of processes relevant information and methods; for example the order.

Domain Driven Design is like sales, it provides one face to the customer.

The surrounding application does access an aggregate through repositories, which are basically some kind of facade. In other words: A domain object is an aggregate if it has a repository. Repositories provide methods to query for aggregates. Examples may be findClientByEmail(string email) or just findAll(). They are as well performing updates and are adding new objects to the domain. Thus, they likely have methods like add(Client newClient) or delete(Client toBeDeletedClient).

With an aggregate you are accessing children only through its parent. For example, a client aggregate gives you access to all orders by the client. But If you need to access the data from another perspective than the client’s, you can establish a second aggregate. Let’s say you want to have a list of all orders, no matter by which client they were placed. An order repository will get the job done!

The domain layer and its repositories.

Since the repository is the entry point for the surrounding application to the domain layer, it’s where other players enter the area. Remember that we are dealing with plain objects for now.

Have you asked yourself how this will become real? This is were infrastructure comes in. DDD is an excellent companion for frameworks, as it’s built on plain objects, with a simple scheme of value objects, entities and aggregates. However, since simplicity is power in IT, we are now in position to outsource the entire infrastructure part. Let’s have a look under the hood, and how DDD can be spread around an application.


Infrastructure and the Domain Layer

You may have noticed that our focus on the domain excluded a persistence layer as well as common things like views or controllers from our to-do list. The entire application may consist of much more complex stuff than just plain objects, and I want to point out a few steps that have to be done to wire the domain and the app together, as well as what implementation strategies exist. I will make some examples based upon FLOW3, an application framework with the main focus of providing DDD-infrastructure. It’s not necessary, but it won’t hurt if you read my introduction. In order to apply the business domain to an application, the following steps are common:

  • Implementing a persistence layer that saves our domain objects – e.g. a MySQL database.
  • Building a repository that abstracts the access to the relational database and provides an easy interface for queries.
  • Build a factory service that generates the objects and builds the aggregate tree.
  • Provide a service infrastructure to introduce non-domain relevant logic.
  • Making the application domain-aware.

When you have a look on the comments on the article to Aspect Oriented Programming (AOP), you will see an interesting discussion about whether or not a framework should add its footprint via comment annotations. The approach in FLOW3 is based upon how Domain Driven Design is implemented. Have a look at this code:

/**
 * A Client
 *
 * @FLOW3Scope("prototype")
 * @FLOW3Entity
 */
class Client {

	/**
	 * The clients name.
	 *
	 * @FLOW3Validate(type="Text")
	 * @FLOW3Validate(type="StringLength", options={ "minimum"=1, "maximum"=80 })
	 * @ORMColumn(length=80)
	 * @var string
	 */
	protected $name;

	/**
	 * Get the Client's name
	 *
	 * @return string The Client's name
	 */
	public function getName() {
		return $this->Name;
	}

	/**
	 * Sets this Client's name
	 *
	 * @param string $Name The Client's Name
	 * @return void
	 */
	public function setName($name) {
		$this->name = $name;
	}
}

This is a very simple class and it does not contain much business logic, but this will likely change once the application grows. FLOW3 is present through some code annotations. It’s defining the class as an entity and adds some validation rules to be applied (this is optional). Note that there is an annotation named @ORMColumn(length=80). This is an information for the persistence layer and we will come back to this in a moment.

FLOW3 uses annotations here to keep the domain clean. You are free to use the class anywhere else, as it’s still a plain object. You may opt to switch to the symfony framework, which uses the same persistence layer (Doctrine), hence the code would nearly work out of the box. By pushing the framework configuration outside of the scope of the PHP interpreter, the domain remains a plain old PHP object. You can reuse it even without any framework at all.

But now that the framework is aware of the object, it can now calculate the requirements for a MySQL database table. In order to store instances of the class client, FLOW3 (and Doctrine as persistence framework) would perform the following steps for you:

  • Create a table with the name client.
  • Add a column named name of the type string with the length of 80 chars
  • Since we did not provide a unique identifier (which is required for entities), FLOW3 would kindly auto-generate one for us and add a table cell for it.

The property definition for the items in our order may look like this:

/**
 * The items.
 *
 * @ORMOneToMany(mappedBy="order")
 * @ORMOrderBy({"price" = "ASC"})
 * @var DoctrineCommonCollectionsCollection<LogisticAppDomainModelItem>
 */
 protected $items;

Notice, that this returns a Doctrine Collection, which is some kind of wrapper for an array, like ArrayLists in Java. Essentially this means, that all elements have to be of the given type, in this case Item. I opted to add an order statement on how I want the collection to be organized (by the items prices).

The counterpart in the Item class could be:

/**
 * The order.
 *
 * @ORMManyToOne(inversedBy="items")
 * @var LogisticAppDomainModelOrder
 */
protected $order;

It’s just the tip of an iceberg, but it should give you an idea on how things can be automated: Doctrine provides a powerful strategy on how to map associations to the tables where it stores the object. For example, since the items would translate to a one-to-many relation (one order may have many items) in the database, Doctrine would silently add a foreign key for the order to the item table. If you decide to add a repository for the item (making it an aggregate), you could magically access a findByOrder(Order order) method. That’s the reason why we did not care about databases or persistence during domain creation—it’s something that a framework can take care of.

In case you are new to persistence frameworks, the way of mapping objects to a relational database is called ORM (Object-Relational-Mapping). It has some performance drawbacks, which are mainly caused by the different approaches that relational databases and the object model have. There are long discussions about it. However, in modern CRUD apps (not only domain driven), ORM is the way to go—mainly for maintenance and expandability reasons. However, you should know your ORM and have a good understanding of how it works. Don’t think you no longer need knowledge about databases anymore!

As you may have noticed, your objects can be quiet complex and have a long traversal line if they have many children, which in turn have many children of their own.

Hence, once the repository retrieves data from a database, they have to be transformed to objects in an intelligent way. Since we have now a persistence layer involved, transformation is much more complex than just instantiating an object. We have to manage the traversal line by reducing the relevant calls to the database to a minimum. Not all children are always needed, so they can be retrieved on demand.

Some objects will be value objects that need to be created only once, which can save lots of memory. That’s why, any domain layer needs a smart factory that generates the objects for you. Hence, in modern frameworks, the new operator is considered to be way too low-level for modern applications. FLOW3 goes a long way to provide the opportunity to instantiate objects with the new keyword, but the background compilation auto-changes plain object creation to powerful object management. Some features that your object manager / factory should be capable of, no matter which framework you use, are:

  • Managing entities and value objects
  • An intelligent way to provide children of objects on demand
  • Injecting dependencies and services

You may have frowned on the last sentence. Throughout the whole article I have emphasized to use plain objects in the domain, and I even violated the “don’t repeat yourself” paradigm and mentioned it several times because it’s so important to DDD. And now I’m telling you that you have dependencies and services that need to be part of your domain…

There’s a sad truth in the real world: there isn’t such a thing like a pure domain. You’ll almost never encounter a client that starts from scratch; so, you have to satisfy circumstances like legacy systems. They may have a horrible implementation but the company can’t get rid of it. You may have to call services and APIs and retrieve data from various third parties, and these legacy systems influence the business domain.

Everything we’ve discussed so far are important, but the question on how a framework solves the dependency to non-domain services is critical to a clean Domain Driven Design. That is the reason why the FLOW3 team spent enormous efforts on the implementation of aspect-orientation; it’s a way to introduce services to the domain without touching the code without violating against the rule of plain old objects. There are other approaches, such as shifting the dependencies between services and the domain to the controller, but aspect oriented programming is by far the most elegant way I know. I’d like to hear your thoughts about this topic!

A good framework can give you a lot of support besides the points I mentioned. For example, FLOW3 transparently hands the domain objects in the view with its remarkably cool templating engine called Fluid. Writing Fluid templates, once the domain is done, is as relaxing as a day at the beach.


Summary

This article is just an introduction to Domain Driven Design. I have presented some of the core concepts, but I have to admit that it may be hard to grasp on theory alone. I want to encourage you to try Domain Driven Design for your own in a real world project. You’ll experience that the domain concepts are very intuitive to use.

I’ve been thrown into DDD like a fish out of water in a very large extbase project, without much prior knowledge of the concepts (extbase is a Domain Driven Design framework for building extensions for the CMS Typo3 and is based on FLOW3). It has broadened my outlook on how to think about software design, and I hope it will broaden yours, too.