Storing files and metadata with NHibernate

Posted by Siim on September 21st, 2010

Now and then there comes a need to save some documents to the database also, along with the other information stored there already. It’s not a big deal to save some additional files to the database, especially when it’s not the main purpose of the application, just some extra. So no need for different storage mechanism.

This time I’m using NHibernate and I thought how I should structure tables and write mappings so that files data gets loaded only then when I really need it (eg. downloading a file). I also save some metadata about the file, some if it has business meaning, some of it doesn’t.

Database

I came up with the following simple schema:

clip_image001

I can load all the related meta-info about the file easily without touching the real content. That kind of schema also allows us to put FILE_DATA table to a different file group in MS SQL Server, so physically it can be located on other drive or partition whereas files metadata is located with our other tables.

Mappings

But this is only database side, there are also NHibernate mappings we have to work on. From NHibernate point of view, it’s not one-to-one relation but more like an one-to-many relation, meaning that File may have 0 or more FileData objects. So internally I mapped it like so, but to the outside I exposed it like one-to-one. I am using FluentNhibernate and here are my mappings for that:

public class FileMap : ClassMap<File>
{
	public FileMap()
	{
		DiscriminateSubClassesOnColumn("FILE_TYPE");

		Id(x => x.Id);
		Map(x => x.Type, "FILE_TYPE")
			.ReadOnly();
		Map(x => x.Name);
		Map(x => x.ContentType);

		HasMany<FileData>(Reveal.Member<File>("_files"))
			.Access.Field()
			.Cascade.AllDeleteOrphan()
			.Inverse();
	}
}

public class FileDataMap : ClassMap<FileData>
{
	public FileDataMap()
	{
		Id(x => x.Id);
		Map(x => x.Content);
		References(x => x.File);
	}
}

And related objects:

public abstract class File : IntegerIdentityEntity
{
	private IList<FileData> _files;

	protected File()
	{
		_files = new List<FileData>();
	}

	protected virtual IList<FileData> Files
	{
		get { return _files; }
	}

	protected virtual FileData FileData
	{
		get { return Files.SingleOrDefault(); }
	}

	public virtual string Type { get; protected set; }

	public virtual string Name { get; set; }

	public virtual string Number { get; set; }

	public virtual string ContentType { get; set; }

	public virtual byte[] Data
	{
		get
		{
			return FileData != null ? FileData.Content : null;
		}
		set
		{
			if (FileData != null && value == null)
			{
				FileData.File = null;
				Files.Remove(FileData);
			}
			else if(FileData != null)
			{
				FileData.Content = value;
			}
			else if(value != null)
			{
				var dataFile = new FileData {File = this, Content = value};
				Files.Add(dataFile);
			}
		}
	}
}

public class FileData : IntegerIdentityEntity,
{
	public virtual File File { get; set; }

	public virtual byte[] Content { get; set; }
}

As you can see, externally there is only one object – File, with property Data which returns the contents of the file as a byte array. Internally it is stored in the FileData collection because it’s the natural way how NHibernate handles such scenarios but that doesn’t mean we must always expose it as collection.

Conclusion

This approach allows to store files and their metadata in a database in a simple way, without adding any extra overload to fetching when it’s not necessary. From a database perspective it don’t add any extra constraints either, because we can put data table to a different partition so keeping the size of the main database reasonable.

ASP.NET MVC RenderAction with action method overloads

Posted by Siim on September 5th, 2010

In ASP.NET MVC, every controller method which is public and not marked with NonAction attribute is considered as an action. And action name is the same as method name. But you can also provide ActionName attribute with specified action name. There is also different attributes to constrain actions to handle only specified HTTP methods (GET, POST, PUT etc). All that information is used when finding which controller method to execute.

So, when technically (in a sense of C#) you can create many method overloads for a controller method, you end up with runtime exception when you don’t further restrain those actions. For example, consider following controller:

public class ProductController : Controller
{
	public ActionResult Edit(int id)
	{
		return View("Edit");
	}

	public ActionResult Edit(int id, ProductItem item)
	{
		return View("EditPost");
	}
}

public class ProductItem
{
	public int Id { get; set; }

	public string Name { get; set; }
}

When executing Edit action, MVC don’t know which method overload to execute, because current route could be handled by both of the Edit methods. But when decorating first method with HttpGet attribute and the second with HttpPost attribute, it works well.

Now, the trouble comes when you execute one of such methods from the view with RenderAtion method. Lets say you have two controllers:

public class ProductController : Controller
{
	public ActionResult Edit(int id)
	{
		return View("Edit");
	}

	[HttpPost]
	public ActionResult Edit(int id, ProductItem item)
	{
		return View("EditPost");
	}
}

public class ProductDetailController : Controller
{
	public ActionResult Edit(int id)
	{
		return View("EditDetail");
	}

	[HttpPost]
	public ActionResult Edit(int id, ProductDetailItem item)
	{
		return View("EditDetailPost");
	}
}

public class ProductDetailItem
{
	public string Type { get; set; }
}

public class ProductItem
{
	public int Id { get; set; }

	public string Name { get; set; }
}

ProductController is used to display product form view. On that form, I also call RenderAction to render Edit method on the ProductDetailController which displays some product sub-detail form. When executing first ProductController.Edit method, all works fine. When I submit product form, then the second Edit method is executed (because it handles POST requests).

At first, it looked like some weird behavior. But after when I gave a thought how MVC resolves which action method to execute, it all works out. It’s because RenderAction uses the same algorithm to resolve actions as a regular action link. So pay attention when using same pattern with controllers and actions.

Building Joomla component with Doctrine, adding views – Part 2

Posted by Siim on March 4th, 2010

In my previous post we set up initial structure for the component and generated models. Now it’s time to add some front-end views. I’ll show you how Doctrine can be used in controllers and how to create clean and simple views.

When using Joomla pattern, then most of the work is done in view’s display() method. I don’t like that. I’d like that my controller provides needed data to the view. So we need to create a base controller from which our component controllers inherit to provide an injection point for our views. View is created in controller’s display() method so we need to override that but most of the code remains same, though.

protected abstract function onViewLoaded(MvViewBase $view);

public function display($cachable=false)
{
	$document = JFactory::getDocument();

	$viewType = $document->getType();
	$viewName = ucfirst(JRequest::getCmd('view', 'topic'));
	$viewLayout	= JRequest::getCmd('layout', 'default');

	$view = $this->getView($viewName, $viewType, '', array('base_path' => $this->_basePath));

	// Set the layout
	$view->setLayout($viewLayout);

	// Display the view
	if ($cachable && $viewType != 'feed')
	{
		global $option;
		$cache = JFactory::getCache($option, 'view');
		$cache->get($view, 'display');
	}
	else
	{
		$this->onViewLoaded($view);
		$view->display();
	}
}

public function getModel($name='', $prefix='', $config=array())
{
	return false;
}

So now controller can be very simple – only load data using Doctrine and initialize the view. Views contains also only setters and getters for the data they need.

class Controller extends MvControllerBase
{
	protected function onViewLoaded(MvViewBase $view)
	{
		switch($view->getName())
		{
			case 'movies':
				$this->dataBindMoviesView($view);
				break;

			case 'movie':
				$this->dataBindMovieView($view);
				break;
		}
	}

	private function dataBindMoviesView(ViewMovies $view)
	{
		$query = Doctrine_Query::create()
				->select('m.*')
				->from('JosMvMovie m')
				->orderBy('m.title ASC');

		$movies = $query->execute();
		$view->setMovies($movies->getData());
	}

	private function dataBindMovieView(ViewMovie $view)
	{
		$movieId = JRequest::getInt('id', 0);
		$movie = Doctrine_Core::getTable('JosMvMovie')->find($movieId);

		$view->setMovie($movie);
	}
}

Doctrine (like most of the ORMs) can load related data automatically so no extra work needed for that. In our case, I can just ask genres from the movie, I don’t need to load them separately. Doctrine also allows tuning how and when relations are loaded – are they lazy-loaded or joined directly when executing query, for example. You can read more from here and here.

Because we have many-to-many relation between movies and genres, automatically generated models need some tuning. We have to specify the relation class name so that Doctrine can detect how it can fill the collections. When looking the definition of BaseJosMvMovie class, which contains mappings with the data model, then there is:

public function setUp()
{
	parent::setUp();
	$this->hasMany('JosMvMovieGenre', array(
		 'local' => 'genre_id',
		 'foreign' => 'genre_id'));
}

We need to add refClass attribute there which defines the class for many-to-many relation.

public function setUp()
{
	parent::setUp();
	$this->hasMany('JosMvGenre as Genres', array(
		 'local' => 'movie_id',
		 'foreign' => 'genre_id',
		 'refClass' => 'JosMvMovieGenre'));
}

Same kind of change needs to be done with the BaseJosMvGenre too, if you want access movies from the genre. Now we only need to add some views to display our movies. They are dead simple.

class ViewMovies extends MvViewBase
{
	private $_movies;

	public function setMovies(array $movies)
	{
		$this->_movies = $movies;
	}

	public function getMovies()
	{
		return $this->_movies;
	}
}

And HTML for that.

<table width="100%" border="1">
	<caption>Movies</caption>
<thead>
<tr>
	<th>Title</th>
	<th>IMDB</th>
	<th>Genres</th>
</tr>
</thead>
<tbody>
<?php foreach($this->getMovies() as $movie): ?>
<tr>
	<td title="<?php echo $movie->plot; ?>"><a href="<?php echo $this->getRouter()->createMovieLink($movie); ?>"><?php echo $movie->title; ?></a></td>
	<td><a href="<?php echo $movie->imdb_link; ?>" target="_blank">IMDB</a></td>
	<td><?php echo join(', ', $movie->Genres->getData()); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>

So this is it for now. Hope it gets you started. In my next post in the series I’ll create views for the back-end, there are things a little bit different, but not much. Full source code with both the views can be downloaded here.

Building Joomla component with Doctrine – Part 1

Posted by Siim on March 1st, 2010

I have done plenty of development on Joomla platform. Although generally it’s pretty good CMS platform for PHP but the part of that I don’t like is it’s MVC implementation. The Model implementation feels just so weird to me. Maybe because I haven’t used to that approach it uses, because I’m more NHibernate guy from .NET space… Anyway, I decided to look for ORMs in a PHP environment and found Doctrine. I did a simple test project with that and decided to use it to develop a simple Joomla component and keep things as simple as possible.

In it’s standard implementation of MVC in Joomla, controller injects needed models to the view and view asks data directly from the models. In other cases (when there is no need for a view) controller uses models directly to accomplish some task. I don’t like that. I’d like when there is just a plain view which is filled with the data by the controller and I would loose the concept of model (in terms of Joomla) wholly. Model layer will be replaced with the Doctrine and it will be used only by the controller not views.

So lets start.

Sample component

For a sample component we use simple movies catalog. It contains models for Movie and Genre. Movie has attributes like name, plot and link to IMDB. Administrators can manage movies and genres from the back-end. From front-end users can browse movies and filter them by genre and name. This should me simple enough to start with. The purpose of this post is to demonstrate how to integrate Doctrine into Joomla, not to demonstrate capabilities of Doctrine. This is the data model:

movies_data_model

Firstly we need to install Doctrine under the Joomla libraries folder. You can download the latest stable version (which is 1.2.1) from here. After downloading extract the contents of the lib directory under Joomla libraries/doctrine directory.

Configuring Doctrine and creating models

First we need to configure Doctrine to use Joomla database settings. Interesting thing here is the table name format. Firstly we use the Joomla configured table name prefix. Secondly we add our own table prefix because we don’t want to use all the Joomla tables (at least in this case). Here is sample code.

$componentTablePrefix = 'mv_';
$config = JFactory::getConfig();
$ormManager = Doctrine_Manager::getInstance();
$ormManager->setAttribute(Doctrine_Core::ATTR_TBLNAME_FORMAT, $config->getValue('config.dbprefix').$componentTablePrefix.'%s');
$ormManager->setAttribute(Doctrine_Core::ATTR_AUTOLOAD_TABLE_CLASSES, true);
$ormManager->setAttribute(Doctrine_Core::ATTR_VALIDATE, Doctrine_Core::VALIDATE_ALL);
$ormManager->setAttribute(Doctrine_Core::ATTR_QUOTE_IDENTIFIER, true);
$conn = Doctrine_Manager::connection(
    $config->getValue('config.dbtype').'://'.
    $config->getValue('config.user').':'.
    $config->getValue('config.password').'@'.
    $config->getValue('config.host').'/'.
    $config->getValue('config.db'), 'default');

Then we use Doctrine to generate object model from the database tables for us. Of course you can create (or update) them manually if you want, there’s a pretty good documentation on that. We use this command to do that.

$modelsPath = JPATH_SITE.DS.'administrator'.DS.'components'.DS.'com_movies'.DS.'models'.DS;
Doctrine_Core::generateModelsFromDb($modelsPath, array('default'), array('generateTableClasses' => true));
Doctrine_Core::loadModels($modelsPath);

Unfortunately Doctrine also adds table prefix to the class name. So table name jos_mv_movie is converted to class name JosMvMovie. Generally I would remove that but currently I leave it as it is.

Creating a component structure

Next we create a bootstrapper script for our component to do all the wiring for us. It will include setup for Doctrine and for our component. We use PHP spl_autoload_register function to provide functionality to load all necessary scripts automatically without the need to scatter our scripts with require statements. Because Joomla uses __autoload function, we also need to re-register Joomla autloader with spl_autoload_register. That’s because __autoload can be used only once but we need different autoload functions – for Doctrine, our component and for Joomla itself.

Note: Because of Joomla isn’t written for PHP5 specifically, you need to explicitly set JLoader::load function to static.

We will use the same bootstrapper for front-end and back-end so we need to create separate configuration options for those. Because in front-end we need to load classes that are found from the front-end directories but also classes that are defined in the back-end, whereas database configuration remains same. And when creating some integration tests you may want to connect to different database.

Here is part of the code for bootstrapper.

class MvBootstrapper
{
    private static $configurationMode;
    public static function configure(ConfigurationMode $mode)
    {
        self::$configurationMode = $mode;
        self::configureJoomla();
        self::configureMovies();
        self::configureDoctrine();
    }
    private static function getModelsPath()
    {
        $modelsPath = JPATH_SITE.DS.'administrator'.DS.'components'.DS.'com_movies'.DS.'models'.DS;
        return $modelsPath;
    }
    private static function configureJoomla()
    {
        spl_autoload_register('JLoader::load');
    }
    private static function configureDoctrine()
    {
        JLoader::register('Doctrine_Core', JPATH_SITE.DS.'libraries'.DS.'doctrine'.DS.'Doctrine'.DS.'Core.php');
        JLoader::load('Doctrine');
        spl_autoload_register('Doctrine_Core::autoload');
        $ormManager = Doctrine_Manager::getInstance();
        $ormManager->setAttribute(Doctrine_Core::ATTR_TBLNAME_FORMAT, self::$configurationMode->getTablePrefix().'%s');
        $ormManager->setAttribute(Doctrine_Core::ATTR_AUTOLOAD_TABLE_CLASSES, true);
        $ormManager->setAttribute(Doctrine_Core::ATTR_VALIDATE, Doctrine_Core::VALIDATE_ALL);
        $ormManager->setAttribute(Doctrine_Core::ATTR_QUOTE_IDENTIFIER, true);
        $conn = Doctrine_Manager::connection(
            self::$configurationMode->getDbType().'://'.
            self::$configurationMode->getDbUsername().':'.
            self::$configurationMode->getDbPassword().'@'.
            self::$configurationMode->getDbHost().'/'.
            self::$configurationMode->getDbName(), 'default');
        Doctrine_Core::loadModels(self::getModelsPath());
    }
    private static function configureMovies()
    {
        MvBootstrapper::registerPath('base', self::$configurationMode);
        MvBootstrapper::registerPath('models/generated', ConfigurationMode::BackEnd());
        spl_autoload_register('MvBootstrapper::autoload');
    }
    public static function autoload($className)
    {
        // Autoload logic
    }
    private static function registerPath($path, ConfigurationMode $mode)
    {
        // Path registration logic
    }
}

Now we need to create startup scripts for our component. It’s nothing special, just a regular component starter page. Here is the code for front-end view.

defined('_JEXEC') or die('Restricted access');
require_once (JPATH_COMPONENT_ADMINISTRATOR.DS.'bootstrapper.php');
MvBootstrapper::configure(ConfigurationMode::FrontEnd());
require_once dirname(__FILE__).DS.'controller.php';
$className = 'Controller';
$controller = new $className();
$controller->execute(JRequest::getCmd('task'));
$controller->redirect();

That’s it for now. In my next post I’ll show how to create controllers and views for the front-end. And how to use Doctrine in controllers and to pass data to a view.

You can download current version of the component from here.

Mapping translations in NHibernate

Posted by Siim on February 24th, 2010

Some time ago I wrote about translations. Now was the time to actually implement that feature. Because I don’t have any default language which is always present on an object, I had somehow create it virtually, so in case of translation is not found in current language, the default one (for a given object) will be used. I decided that the first language for an object (that is, the language in which the object was created), is used as a default one.

So my data model looks like this. Firstname_id and lastname_id both refer to the translation table as foreign key relations.

data_model_loc

All the objects were mapped as entities and I was trying to map relation between Translation and it’s Localizations as an ordered list. But I soon discovered that ordered collections don’t support bi-directional associations natively. I had a bi-directional relation between Translation and Localizations. So I had to do index handling myself. For that I created a Index property to Localization which is mapped to order_index column. Property looks like this:

public virtual int Index
{
    get
    {
        return Translation.IndexOf(this);
    }
    private set
    { }
}

And Translation object looks like this:

public abstract class Translation
{
    private IList<Localization> _localizations;
    public Translation()
    {
        _localizations = new List<Localization>();
    }
    public abstract string Context { get; }
    public virtual IEnumerable<Localization> Localizations
    {
        get { return _localizations; }
    }
    public virtual int IndexOf(Localization localization)
    {
        return _localizations.IndexOf(localization);
    }
    public virtual string DefaultValue
    {
        get
        {
            var loc = _localizations.FirstOrDefault();
            return loc != null ? loc.Value : null;
        }
    }
    public virtual string GetCurrentValue(Language language)
    {
        return this[language] ?? DefaultValue;
    }
    public virtual string this[Language language]
    {
        get
        {
            var localization = _localizations.SingleOrDefault(x => x.Language.Locale == language.Locale);
            return localization == null ? null : localization.Value;
        }
        set
        {
            var localization = _localizations.SingleOrDefault(x => x.Language.Equals(language));
            if (localization != null && !string.IsNullOrEmpty(value))
            {
                localization.Value = value;
            }
            else if (localization != null)
            {
                RemoveLocalization(localization);
            }
            else if (!string.IsNullOrEmpty(value))
            {
                AddLocalization(language, value);
            }
        }
    }
    private void RemoveLocalization(Localization localization)
    {
        _localizations.Remove(localization);
    }
    private void AddLocalization(Language language, string value)
    {
        var localization = new Localization(language, value) { Translation = this };
        _localizations.Add(localization);
    }
}

As you can see, I added indexer property to Translation to manipulate with localizations conveniently. Although I’m not sure if using an object as an indexer has any drawbacks later on… Thoughts welcome.

You may have also noticed abstract Context property on Translation. It isn’t strictly required, but I found it useful in my implementation. By using this I can conveniently ask all the translations for person name or for product name, for example. This is useful when users need to translate all product names in a batch, so I can display them on a single form.

And here are example NHibernate mappings:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<!-- Translation -->
<class abstract="true" name="Translation" table="[translation]">
    <id name="Id" access="property" column="translation_id">
        <generator class="identity" />
    </id>
    <discriminator column="context"/>
    <list name="Localizations" cascade="all-delete-orphan" fetch="join" access="field.camelcase-underscore" inverse="true">
        <key column="translation_id" />
        <index column="order_index" />
        <one-to-many class="Localization" />
    </list>
    <subclass discriminator-value="PersonFirstName" name="PersonFirstNameTranslation">
    </subclass>
    <subclass discriminator-value="ProductName" name="ProductNameTranslation">
    </subclass>
    <!-- etc -->
</class>
<!-- Localization -->
<class name="Localization" table="localization">
    <id name="Id" access="property" column="localization_id">
        <generator class="identity" />
    </id>
    <many-to-one name="Translation" class="Translation" column="translation_id" not-null="true" />
    <many-to-one name="Language" class="Language" column="language_id" not-null="true" />
    <property name="Value" column="value"/>
    <property name="Index" column="order_index" type="int" update="true" insert="true" />
</class>
<!-- Person -->
<joined-subclass name="Person" extends="Party" table="person">
    <key column="party_id" />
    <property name="PersonalCode" column="personal_code" />
    <property name="DateOfBirth" column="birth_date" access="field.camelcase-underscore" />
    <many-to-one name="FirstName" column="firstname_id" class="Translation" fetch="join"
        access="property" cascade="all-delete-orphan" not-null="true"/>
    <many-to-one name="LastName" column="lastname_id" class="Translation" fetch="join"
        access="property" cascade="all-delete-orphan" not-null="true"/>
</joined-subclass>
</hibernate-mapping>

And storing a new object with translations is simple as this:

var person = new Person();
person.PersonalCode = "12345670";
// languages is IList<Language>
foreach (var language in languages)
{
    person.FirstName[language] = "First name: " + language.Name;
    person.LastName[language] = "Last name: " + language.Name;
}
session.Save(person);

This solution has it’s own drawbacks also, but I found that it’s best for my needs. Currently translations are not reusable. By that I mean when one term is used in multiple places, it has to be maintained separately on each instance. Of course, I can make user to choose from existing translations, but it seems to me that this makes things overly complicated. But again, it depends on the exact context :)

Setting up TeamCity build environment

Posted by Siim on December 6th, 2009

We are using CruiseControl.NET as our CI build server for a while now and it is working fine. CC.NET displayed test results of NUnit and included NCover, NDepend, Simian and FxCop reports for code analyzes. Also produced deployment package and documentation. For build script we are using NAnt which configuration is stored in source control along our projects so all can be controlled in one place. There are only few cons with Cruise Control – it’s based on XML configuration and it doesn’t include proper account management with access control.
So I thought I would try some other CI server and because I heard lot about TeamCity I decided to give it a shot.

Firstly, installation of the TeamCity was smooth and I got it running quickly. Internally it’s based on Java and runs on built-in Tomcat server. By default it uses a internal storage engine but it’s strongly suggested that in production it should be reconfigured to use some proper database like MSSQL or MySQL. I chose MSSQL because it was already installed on the server. I followed instructions on the documentation and got it running. Read the rest of this entry »

Repository and Query Object

Posted by Siim on November 19th, 2009

There has been some discussion over the topic already and there are different opinions. I have been using Repository model all the way so far. I have implemented generic base repository for all common operations and when there are some specific needs (view specific query for example!) for some types then I’ll create specific repository. I know that repositories should be created only for aggregate roots but there I have encountered a little problem. View needs to display data from different entities (aggregates or not) so I cannot always follow the principle of repository per aggregate root. So this poses a problem here – repositories are cluttered with view specific needs. And moreover – repository methods are decorated with sorting and paging parameters which definitely shouldn’t be there. So I decided to change that.

There have been some discussion about using multiple models of the domain for different purposes. For example different kind of a model for reporting purposes. It can even be different kind of data store for that model. Udi and Ayende (didn’t find exact posts anymore) have blogged about that before. Complex views with special needs can be also considered as a way of reporting. In my case, using wholly different model for a view is kind of a overkill so I try to leverage the problem by using query objects separated from the repository. They don’t use repositories by themselves so query objects can be viewed as "specific repositories for views" and they live only in presentation layer. And they don’t operate with domain objects, only with view specific objects like DTOs, filtering specifications. By using this kind of approach, writing complex view specific queries should be pretty easy. I’m not tied to NHibernate criteria, I can use HQL or even ADO.NET. Currently I’m using HQL which returns directly DTOs but I’m thinking of giving a try to LINQ for NHibernate.

When using repositories, I used Expressions to represent sorting and filtering specification. But this seems now overly complex when considering that there were some heavy mappings from string format to Expression format. So now, when it’s only a presentation concern, I can use plain strings, just like they come from the view and do the proper mappings in the query object itself. Like they say – KISS.

Multilingual content in DDD using NHibernate

Posted by Siim on October 29th, 2009

There are not so many business applications which need multilingual content. I exclude any kind of CMS’s, they aren’t “true” business application anyway, in my opinion. But the current project I’m working on has that kind of requirement – most of the data must be available in multiple languages and users should easily and intuitively browse and edit that data in current working language. Part of this requirement is not so easy to achieve. Especially when there is a constraint that there cannot be a default language in it’s standard term – object may be available in any language and even only in that language.

Currently we are using a single translations table where translation is identified by it’s context which is mostly some sort of object/field key or basically can be any string. All the translatable fields are stored in the entity table also as a “original value”. This solution is far from perfect and do not fit very well into domain model in general. It was meant to be a quick draft of the solution.

Usually, this kind of translation should be invisible in domain model perspective. But in my case I think it should be reflected in domain model also, because it IS one of the purpose and requirement of the business application. There are two possible viewpoints to consider – a technical perspective and a user’s point of view. I’ve explored different models that people have used before and I think that the solution that Davy Brion describes seems the best one.

It is tied to the domain object. That means there are direct relation between entity and it’s translations. It simplifies CRUD operations on it, NHibernate can take care of it itself. Searching from localized versions are also quite simple, performance overhead is minimal.
It uses a single translations table. Actually there are couple of tables, but all translations are stored and accessed from one place. This makes later maintenance simpler and don’t create so much noise in the domain model (and in data model also).
It’s quite simple to extend and it fits perfectly into NHibernate. I find that for my solution I should also apply the default language attribute on the translation, because for different object, the term default language can mean different language from what is global default languages, if such exists.

When using this model, every translatable property (a product’s name for example) is an object which holds at least one localization. We don’t have a term original like I have seen in some models, every value is a localization. This makes things simpler in users point of view.

I don’t write about any technical data, you can check that on Davy’s blog, but I will update this blog post when I do any changes to this model. And I’ll try to provide some samples then.

Why in-house frameworks are bad

Posted by Siim on August 26th, 2009

People like to think that having an in-house framework for all the projects with all available different kind of 3rd-party libraries is a good thing. Hey, you can save a bunch of time when you doesn’t have to write common code over and over again, don’t you?

Well, things aren’t so simple. Having some extras always causes some burden on the maintenance. There are plenty of reasons to prove that:

  • Framework should be developed separately from projects using it
    It adds extra overhead to update the framework, because someone must check if the proposed change fits in the framework. And after the change, projects which use the framework should also be updated. It all takes extra time which could be avoided.
  • Who is the charge of the development?
    Someone must be the charge of the framework or it ends like a /dev/null – holding bunch of stuff that nobody uses. The one should be responsible of selecting features to add or not to add.
  • Tracking framework versions
  • Projects have different requirement to fit all
    Most of the projects are still quite different by their requirements, so framework may finally contain much code which is used by the one-two separate projects only
  • It takes time to develop correct and working framework
    All takes time, so instead of creating business value, we spend time to create some internal framework which, in the end, is used only by the single project. What a waste of the resources.

I have participated in a quite a few projects where common frameworks were a hot topic and we tried to make all things so general that it would fit in the framework. But in the end we spent more time on developing the framework than creating the business value, so the projects delayed. There were times when I personally also believed that a framework is a must and tried to create a usable framework before creating some real value (also in my personal projects).

I’m not saying that all the frameworks are bad, I just think that developing your own framework may not be such a good idea. In my opinion, such frameworks should be open source, so the community can be charge of the road.


Copyright © 2007 Siim Viikman's blog.