Domain-Driven Design

January 2, 2010 DDD 6 comments

Lets start with the following question: Where is a car on the left or on the right?
 
You would probably say that Car is on the Right, and you are correct. But hold on. What is the difference between them? Difference is that Car is not just a huge collection of parts. Car is not even a collection of parts which need to fit together, but a car is much more than that. A good Car starts with vision and carefully written specifications, and it continues with design and with testing. The design is modified based on the testing results. Only after that each part could be assembled together.

Software development is similar, we cannot just sit down and type code.

What is Domain-Driven Design?

We all want to develop our applications, but business requirements nowadays are huge, and we dive in that complexity. And we can get stuck will all of that, because the whole complexity is in the Domain. For example banking system. Do you as a software developer want to know how does it work? Probably no, but you still need to develop your system. The guy from bank stands near you and and he wants you to develop application for him. What is your starting point? Is it table in database or is it Model?

 Domain-Driven Design (DDD) is is an approach to the design of software, based on the two premises:
* that complex domain designs should be based on a model, and
* that for most software projects, the primary focus should be on the domain and domain logic (as opposed to the particular technology used to implement the system).

In other words the heart of the DDD is Model and you start developing your application with drawing your model. Model and design you create should shape each other. Model should represent knowledge to the business and it is language your team speak.
To be able effectively design you should build your Model and implementation together in the same time, cultivating a language based on the Model. You should represent knowledge in your Model and continuously distill it. You could do refactoring to it doing brainstorming and experimenting.

Ubiquitous Language

So, imagine that guy from bank, say Domain Expert, stands near you and you start talking. How does that conversion look like? It could be fractured and this creates serious problems to your project. Domain Experts have their own jargon and the development team has its own. After that daily discussions are disconnected from  the terminology embedded into code. So effect of need to translate what you are talking about into code appears, plus misunderstandings possibilities. This all could lead to not good results.
Need for some common language shows up. Eric Evans calls this language as Ubiquitous Language. Your language is your model, this means that both Domain Experts and Developers should talk the same language which is build on the model. And changes to the Ubiquitous Language means changes to the Model. If Experts start using new terms this should be represented in your model, in your code, diagrams and speech.

How could it look?
* Speech (This means daily conversations between team members.)
* Diagrams (Often these are quick diagrams on the whiteboard, which you created with your Experts.)
* Writing (This could be NOT long documents, describing the Model.)
* UML (But there is no place for cumbersome one.)

Let us move to the way you will build your application.

Layered Architecture

I hope that everybody heard about the Layered Architecture. For the DDD it is very important since we explicitly separate Domain Level, which lives between our Infrastructure and Application layers, like on picture. Note, that isolating of your Domain Level is very important.

User Interface (Presentation Layer)

Responsible for presenting information to the user and interpreting user commands.

Application Layer

This is a thin layer which coordinates the application activity. It does not contain business logic. It does not hold the state of the business objects, but it can hold the state of an application task progress.

Domain Layer

This layer contains information about the domain. This is the heart of the business software. The state of business objects is held here. Persistence of the business objects and possibly their state is delegated to the infrastructure layer.

Infrastructure Layer

This layer acts as a supporting library for all the other layers. It provides communication between layers, implements persistence for business objects, contains supporting libraries for the user interface layer, etc.

For example, a typical, the interaction of the application, domain and infrastructure could look like this. The user wants to book a flights route, and asks an application service in the application layer to do so. The application tier fetches the relevant domain objects from the infrastructure and invokes relevant methods on them, e g to check security margins to other already booked flights. Once the domain objects have made all checks and updated their status to “decided”, the application service persists the objects to the infrastructure.

The Smart UI “Anti-Pattern”

All this sounds good but simplicity of just calling infrastructure lead into temptation, and after that you see that code generated on button press actually executes hard-coded SQL, less terrible it could be when that is masked under some “helper” classes. But that all is manifestation of the the Smart UI “Anti-Pattern”. Could be surprising to you, but it has its advantages/disadvantages. Why not. It will not be a “Anti-Pattern” if it has no advantages.

Advantages:

  • Productivity is high for small applications.
  • Less capable developers could work with application easily.
  • If each screen has its logic it is possible to quickly change product to fit new requirements.
  • Maintenance guys can quickly find root of any bug – just put breakpoint on button press. :)

Looks like a lot of advantages, but let see what are disadvantages. Hope this will convince you to do not use this “Anti-Pattern”.

Disadvantages:

  • Integration of applications is difficult except through the database.
  • There is no reuse of behavior and no abstractions on business problems.
  • Lack of abstractions creates limits to possibilities of refactoring or doing work iterationaly.
  • Complexity buries you quickly. If your application grew to the enterprise level and you still have this Smart UI, then you are in a$$.
  • And finally you will not be able to migrate to any other design except of replacing each applications.

So, don’t lose your mind, if you build your application with layers, do it correctly!

Building blocks of Domain-Driven Design 

Eric Evans introduces terminology to the Domain-Driven Design. Accordingly to him there are such building blocks of the DDD: Associations, Entities, Value Objects, Services, Modules (which are parts of the Model) and Aggregates, Repositories, Factories (which deal with Life Cycle of a Domain Object).We will talk about each of the elements.

Associations

If your Car has four tires this is association of the model of your Car. This is relation between business elements you work with.

Entities

If we were to implement the concept of a Person using a software program, we would probably create a Person class with a series of attributes: name, date of birth, place of birth, etc. For example if you create Patient entity in code it will be represented like Patient class. It is required that for medical system to identify if the patient is the same. Is the patient “Andriy Buday” physician deals with, the same as “Andriy Buday” nurse has a scheduled procedure? So basically Entity is object that has Attributes and Identity.

Value Objects

Value Object are similar to the Entities expect that they do not need Identity, which is required for Entity. For example “Ukraine, Lviv” which is Address is Value object since we do not need identity for it. We could have 300 patients in Lviv (Entities) who could share the same address. This (sharing) could even be done thought the code using Flyweight design pattern to reduce memory needs. But Address is not always Value Object. This depends on “Who is asking?”. For the post system Address could be an Entity.

Services

Consider that your Model has nouns and verbs (they are used in Ubiquitous Language). You could imagine that nouns are Entities and Value Objects. Your verbs are Services. If you have action or operation related to the Domain concept, but is not natural part of any of your Entity or Value Object you could move it into separate item, which is called Service. Interface of your Service should operate with terms of of the domain model. And the operations it does are stateless.

Modules

For a large and complex application, the model tends to grow bigger and bigger. The model reaches a point where it is hard to talk about as a whole, and understanding the relationships and interactions between different parts becomes difficult. For that reason, it is necessary to organize the model into modules. Modules are used as a method of organizing related concepts and tasks in order to reduce complexity. In code this could be packages or namespaces or even simple folders. The advantage of this is that when you place some classes together in a Module, you are telling the next developer who looks at your design to think about them together.

Aggregates

An Aggregate is a group of associated objects which are considered as one unit with regard to data changes. The Aggregate is demarcated by a boundary which separates the objects inside from those outside. Each Aggregate has one Root. The Root is an Entity, and it is the only object accessible from outside.

The picture shows that your Customer code could access Tire only though the Car instance and there is no other way to do that, since Car is Aggregate to Tire. But could be that part of Car need identity. This could be Engine of your Car, since it has its number and you need to form you taxes. In this case Engine is Entity which lives outside of your Aggregate.
We need to deal with Aggregates since they has direct relation to the Life Cycle of Domain Object.

Factories

Creation of an object can be a major operation in itself, but complex assembly operations do not fit the responsibility of the created objects. This operations could be accomplished with Factories. But not always it should be separate object. In scope of DDD Factory could even be a simple constructor. Two major requirements to the Factory are: Atomicity – this means that operation of creating an Entity should also create whole Aggregate associated with it. Abstracted to the type desired – this means that Factories should return you types rater than concrete classes. (i.e. Interfaces).

Repositories

Repository is an object which provide you CRUD operations to your Aggregates.
Repositories have many advantages, including the following:

  • They present clients with a simple model for obtaining persistent objects and managing their life cycle.
  • They decouple application and domain design from persistence technology, multiple database strategies, or even multiple data sources.
  • They communicate design decisions about object access.
  • They allow easy substitution of a dummy implementation, for use in testing (typically using an in-memory collection).

To be more clear how the Client code, Repository and Factory are interacting between each other. Lets take a look on the next sequence diagram:

I hope that diagram is self-descriptive.

Refactoring Toward Deeper Insight

Since the business and its rules are very complicated you are not able to see whole picture at once. If there are some incomes to your Ubiquitous Language you should represent this into your Model. You do this with refactoring of existing code in way that brings key concepts into light.
There are three ways to bring concepts into light:

  • Constraint. For example you could put Constraint into separate method to make it more explicit.
  • Process. This stands for introducing your Services. If process is difficult we can encapsulate algorithm into object and use a Strategy.
  • Specification. A Specification is used to test and object to see if it satisfies a certain criteria. Specification can be used when you need to to select a certain object from a collection like a condition.

Your development process should always be in continuous refactoring. Any changes to the matter you talk with your Experts should be immediately represented in code. If you see that some term starts occur too often just create an Entity or Service for it – bring it to the Light.

Preserving Model Integrity

Imagine a big enterprise project. Could you imagine it with one model which is understandable for whole team, say 200 people? Most commonly such big projects are divided to smaller teams and which of them works on own model. So whole project consists of couple of models and some of them are intersected with other models, some of them uses other models and all of this is the Integrity of your Models.

Bounded Context

Each model has a context. When we deal with single model, the context is implicit. There is no need to define it. When we create an application which is supposed to interact with other software, for example a legacy application, it is clear that old application has its own model and context, and they are separated from the legacy model and its context. They cannot be combined, mixed or confused. When we work on enterprise project we need define the context of each model, we create. Also a model should be small enough to be assigned to one team. Only this way we could keep knowledge up to date and integrated into system.
Whole project should be assembled continuously using Continuous Integration.

Iterations between different contexts

You could probably say that you cannot have 100 models in your project without intersections between them. And you are right. Eric Evans sees few variants of such intersections.

Shared Kernel

The purpose of shared kernel is to reduce duplication, but still keep two separate contexts. Designate some subset of the domain model that the two teams agree to share. Of course this includes, along with this subset of the model, the subset of code or of the database design associated with that part of the model. This explicitly shared stuff has special status, and shouldn’t be changed without consultation with the other team.

Customer-Supplier

There are times when two subsystems have a special relationship: one depends a lot on the other. You could imagine core functionality system and reporting system. Probably reporting system depends on the core one. In such case it is very and very important that team which works on core will be supplying reporting team. That is simply needed.

Conformist

This is much like the Shared Kernel, but there is an important difference. The customer team cannot make changes to the kernel. They can only use it as part of their model, and they can build on the existing code provided.

Anticorruption Layer

We should build an Anticorruption Layer which stands between our client model and the external one. This layer works as a two way translator between two domains and languages. This layer could be needed when we deal with legacy/external model and have no ability to do changes to it.

Open Host Service

The solution is to see the external subsystem as a provider of services. If we can wrap a set of Services around it, then all the other subsystems will access these Services, and we won’t need any translation layer. The difficulty is that each subsystem may need to interact in a specific way with the external subsystem, and to create a coherent set of Services may be problematic.

Infrastructure

Persistence Ignorance

One of the key requirements to your Infrastructure to be able deal with DDD is Persistence Ignorance. This means that once you have your Infrastructure setuped you no longer need to care about how your data goes to database and how it is saved there. Also it is commonly used with Unit Of Work Pattern. This could be accomplished with some of the ORM frameworks like Hibernate.
How do we classify ORMs? We look on the ways how it solves different problems which occur when you map you object-oriented data to relational database, but yea.. there are ORMs which map to the Object databases. But the most common variant is when you have Relational database. So wee take a look how ORM resolves Inheritance issues, what is the querying language of it, does it support all common databases operations like aggregating, ordering, grouping, etc.
Basically all ORMs has Identity Map and Cache strategy they allow us work using Unit Of Work pattern and they could provide Lazy Load to do not hit database often.
NHibernate is the ORM which all the features required to quickly build an advanced persistence layer in your code.

Patterns and practices compatible with Domain-Driven Design

Nowadays Agile is a way to develop software and DDD is very comfortable with it and with technics of the Agile like TDD.

TDD

Test-Driven Design is very good thing to use with DDD. Why? Because writing test before means that your are describing your model just like it was described on the meeting with your Experts. There are even enthusiasts who wants to rename TDD to BDD (Behavioral-Driven Design).

Advantages for design

  • More client control
  • More Interfaces and Fabrics
  • More concrete
  • Low coupling

Other advantages

  • Another version of API
  • Long term support
  • Documentation
  • Like smart compiler

Disadvantages

  • UI & Database testing
  • You feel too much safe
  • Possilitity to lose whole visibility
  • More code to maintain

Designing architecture

While designing your architecture with DDD you often uses such patterns or approaches like:

SOA (Service-oriented architecture)
Generally speaking, SOA is a flexible set of design principles used during the phases of systems development and integration. It provides set of Services with which you are working.

AOP (Aspect-oriented programming)
In computing, aspect-oriented programming (AOP) is a programming paradigm in which secondary or supporting functions are isolated from the main program’s business logic. It aims to increase modularity by allowing the separation of cross-cutting concerns, forming a basis for aspect-oriented software development.

IoC (Inversion of Control and Dependency Injection)
Inversion of Control is principle with which you could easily resolve your dependencies in code delegating setup to some other part like IoC framework (StructureMap for example).

For the end I want to mention that my story is based on books by the link below.
http://domaindrivendesign.org/books
I read all of them (except of duplicated ones :) ) and recommend you to read them also. This will bring a lot of interested stuff to your mind. For example I could state that now I understand better the design of subsystem with which I work on enterprise project. Future is for Domain-Driven Design.

P.S. I hope that you enjoyed my story. Do not hesitate to leave your comments.


6 comments


Applying Domain-Driven Design and Patterns: With Examples in C# and .NET

January 2, 2010 Book Reviews No comments

I just finished reading book “Applying Domain-Driven Design and Patterns: With Examples in C# and .NET” and I liked it.
This book talks about modern key concepts needed
to develop successful product of enterprise level. It describes how to build
your project without losing business needs out of view – you just start with
your Domain level. You develop with TDD and describing behavior of application.
Things with which you are operating are Customer and Order, but not
TBL_CUSTOMER and TBL_ORDER – this is one of key aspects, which means hiding
your infrastructure level and interacting with database from developer’s eyes,
which can be easily accomplished with NHibernate.
Book is excellent in examples which Jimmy presents. I haven’t seen
so good explanation of the State design pattern as it is in this book.
Book covers a lot of other interesting things like IoC, AOP, SOP,
MVC and so on… really lot of stuff under another light you ever seen. You need
to read this book if you are going to grow and keeping on track.
P.S. BTW as you see I read it in Russian (not my native language also :) but I have it paper variant). 


No comments


Where Do You Want to Be In a Year?

January 1, 2010 Success, YearPlanReport 3 comments

Right now is the first time in this year when I opened google reader and found one interesting post Where Do You Want to Be In a Year? by Tim Barcz. When I read it I realized that I had read book The 7 Habits of Highly Effective People also.

In general all books on Success says that you need to “Begin with the end in mind“, this means that you must have your plan.

At first you need to find out you goal for whole life. That is your target and main thing you need to get. Then you need to do breakdown of tasks you need to accomplish in your life, having them in few levels of term scope like 10 years term, 5 years term and 1 year term… and so on. So question:

Where Do You Want to Be In a Year?

My answer: 

By the end of 2010 I want:

1. Get Microsoft Certification:
  * MCTS (Exams: 70-536 and 70-505)
  * MCPD 70-563 and 70-565 (this one is big fish, but I need such!)
2. Read at least 24 books.
3. Become known employee in my company, sharing knowledge and doing presentations, so will have much more authority among co-workers.
4. Familiarize with Java and contribute research and development work to Kohonen Maps world.
5. to be Senior Developer and continue growing…
6. Improve my English skills to have at least upper-intermediate strong level (according to my company graduating)
7. Gather good capital and looking for investments of money.

Write down your answer in comments or give me link to your blog!


3 comments


Ensure that your Unit Test is true inspector of bad code

December 29, 2009 UnitTesting No comments

Few days ago I did refactoring and as I’m quite sure that we have good coverage I checked-in my code after all tests passed.
But today QA from my team stated that he/she cannot save new Customer. I dived into log files and found that exception occured on SaveCustomer method.
Ok, I moved to the appropriate UT. (Note: it passes ok.) Which looked like:

[Test, Category(“Integration”)]
public void SaveCustomer_EnsureWeCanCreateNewCustomer()
{
    Customer customer = new Customer();
    //some another setup code here…
    try
    {
    //we hit database to save newly created customer
        savedCustomer = CustomerRepository.SaveCustomer(customer);

    //now we fetch customer from database requesting with the ID of just saved customer
        fetchedCustomer = CustomerRepository.FetchCustomer(savedCustomer.ID);

    //if fetched customer is not null we are ok!
        Assert.That( fetchedCustomer, Is.Not.Null );
    }
    catch
    {
        //do nothing here….
    }
    finally
    {
        if ( fetchedCustomer != null )
        {
        //we want to leave our database clear after test run
            CustomerRepository.Delete( fetchedCustomer );
        }
    }  
}

This Unit Test will be succeded anyway, because
Assert.That(fetchedCustomer, Is.Not.Null) and similar asserts generates AssertionException.
Honestly I’m not aware why there was that catch{} piece, but anyway you could put there two catch blocks if it is really needed – one specific for the AssertionException and another for all cases of our life.
When you write your Unit Test you need to see it in both states – first
in failure and then in success. After that you can allow yourself
commit that code.

Main idea is:

You always need to keep one’s eye on quality of you Unit Tests and be sure that your Unit Test will fail when that is needed.


No comments


How to create and apply a patch with Tortoise SVN

December 27, 2009 SVN No comments

What is patch?
Patch is text file which contains only differences you did to code, so it will contain changed and new lines as well as removed all in one bulk text file (*.diff or *.patch).
Why to use it?
There could be few cases when you would need patch. Next are three that I see:
1. For example it is possible that you do not have write access in SVN in case if you are contributor to some open source project. So you can send you changes as patch to those who have rights.
2. Another case could be when you lost your connection on one machine but want to port your changes to another machine easily and then commit them. (I faced with this one).
3. One another interesting usage could be establishing code reviews via patches. It could be that you have command that works remotely, and to ensure that we commit only good code your code reviewer would like to have your changes on his machine quickly and easily. Of course it it ugly to sent him all files you have changed and with screenshots of differences.
Create patch
You just need to be sure that you have latest code, that your changes are done and after that just right click on folder on which you want to create patch with selecting “Create patch…” like above. This will take you to dialog where you select files you want to include into your patch and then will take to save file dialog. So you could get file like validationRules_CodeReview.patch.
Apply patch 
Applying patch is also very easy, just pick up “Apply patch…” from the context menu of TortoiseSVN of folder needed (should be same folder as patch was created). After you selected patch file you will get two windows – one is TortoiseMerge on background and another is File patches. File patches window is list of files to patch. Double click on file from list opens differences in background window, so you could review them. And to apply you just need right click and select “Patch all” or “Patch selected“.

Quick google with “How to create and apply a patch with Tortoise SVN” gives me next: http://tortoisesvn.net/docs/release/TortoiseSVN_en/tsvn-dug-patch.html so maybe this will make more sense for you :)


No comments


Quick Verification if row exists in database with Nhibernate

December 23, 2009 NHibernate No comments

Next SQL, which verifies if customer row exists in database:

SELECT customer0_.CUSTOMER_ID AS x0_0_
FROM   T_CUSTOMER customer0_
WHERE  (customer0_.CUSTOMER_ID = 123456)

Will be generated with next NHibernate Query:

public bool CustomerExistsInDatabase(Int32 customerId)
{
    var result = Session.CreateQuery(string.Format(“select c.CustomerID from Customer c where c.CustomerID = {0}”, customerId)).UniqueResult<Int32?>();

    return result.HasValue;
}

Please note, that you could use not only primary key, but any other property in your where condition.


No comments


Regex Quick Example

December 22, 2009 .NET, Regex No comments

This is just another quick example of Regex pattern.

Next pattern matches “at least one symbol which is letter or digit at the begging and then exactly one delimiter ‘|’ and then at least one symbol which is letter or digit in the end“:

var valuePattern = new Regex(@”^w+|w+$”);
bool isValid = valuePattern.IsMatch(someStringValue);

Valid is:

ab123|cde45

Not valid are:

|ad
df|
adf||adf

and so on…

Links I’ve used:

Verify your Regex:  
http://www.pagecolumn.com/tool/regtest.htm
Cheat Sheet:
http://www.addedbytes.com/download/regular-expressions-cheat-sheet-v2/pdf/


No comments


Code Review: try – catch

December 22, 2009 CodeReview 2 comments

Sometimes it is hard to convince that catch(…) is not so good!

Today I provided code review to one of my colleague and he was totally sure the he need try{/*code*/}catch(){} in few places.

First looked like:

try
{
   int someValueId = Convert.ToInt32(value);
   if (someValueId > 0)
   {
     return new ValidationResult(true, “Valid”);
   }
}
catch{}

I rewrote code to:

int serviceId;
Int32.TryParse(value, out serviceId);
if (serviceId > 0)
{
  return new ValidationResult(true, “Valid”);
}

Then I spent few minutes to convince him that this code is ok and that this is not good to have catch for everything.

Another code looked like:

string result = string.Empty;
try
{
    result = documentContent.Descendants(“SomeValueCode”).FirstOrDefault().Value;
}
catch (Exception)
{
    //He was trying to ensure me that since we are doing validation we should not throw an exception
}
//do some work here

Now I tried to explain that FirstOrDefault() returns us either first element of collection OR null, so if we got null we just need to do some another stuff,
but if some exception will occur we should allow it fly out OR write some specific catch for exceptions that could be thrown. Code which I propose looks like:

var resultElement = documentContent.Descendants(“SomeValueCode”).FirstOrDefault();
if(interventionCodeElement != null)
{
    var result = resultElement.Value;
    //do some work here
}

After conversation we decided that try(/*code*/)catch() is not so good.


2 comments


AssertDatesAreEqual

December 21, 2009 UnitTesting No comments

Have you ever faced with UT that failed once and then you always see it succeeded?

One of such tests could be test where you verify that date properties of some newly created entity are equal if they take DateTime.Now for that.

Today Hudson showed me that some test failed in project I worked near half a year ago, so test looked like:

var item1 = new Item();
var item2 = new Item();
//…
Assert.AreEqual(item1.CreatedDate, item2.CreatedDate);

Test failed because time difference between dates was greater than few milliseconds. This could occur when processor is too occupied.

So I changed assert call to more realistic:

public void AssertDatesAreEqual(DateTime dateTimeLeft, DateTime dateTimeRight)
{
     Assert.IsTrue(Math.Abs((dateTimeLeft dateTimeRight).Milliseconds) < 100,
    “TimeSpan difference between dates is greater than 100 milliseconds.”);
}


No comments


NHibernate: The column name COLUMN_ID is specified more than once in the SET clause

December 9, 2009 NHibernate 4 comments

Sometime ago I faced with NHibernate issue and spent much time on figuring out how to resolve it. Issue was with saving complicated entity.
Error:

The column name ‘RESOURCE_ID’ is specified more than once in the SET clause. A column cannot be assigned more than one value in the same SET clause. Modify the SET clause to make sure that a column is updated only once. If the SET clause updates columns of a view, then the column name ‘RESOURCE_ID’ may appear twice in the view definition.

With NHibernate Profiler I found that this fails on the next SQL: 

UPDATE SOME_TABLE SET SOME_COLUMN1 = ?, SOME_COLUMN2 = ?, RESOURCE_ID = ?, SOMETHING_ELSE = ?, RESOURCE_ID = ? WHERE SOME_TABLE_ID = ?

With better look you will see that there two times RESOURCE_ID mentioned so this looks like wrong mapping. But I was sure that everything is ok there.

This two References are wrong.
At first glance do you see why? At that moment I was not able also.

References(x => x.Resource)
        .Access.AsCamelCaseField(Prefix.Underscore)
        .WithForeignKey(“RESOURCE_ID”).TheColumnNameIs(“RESOURCE_ID”)
        .FetchType.Join();

References(x => x.ResourceRole)
        .Access.AsCamelCaseField(Prefix.Underscore)
        .WithColumns(“RESOURCE_ROLE_ID”, “RESOURCE_ID”)
        .FetchType.Join();

Here Resource has key (RESOURCE_ID).
ResourceRole has composite key (
ROLE_ID and RESOURCE_ID).

After long searching and many tries I found why mapping is not correct. Even have my explanation for it.

When we reference ResourceRole we already use RESOURCE_ID field, so when we setup Resource and say “Hey, there is one more RESOURCE_ID“, then NHibernate cannot find out how to update all this correctly.

Solution:
insert=”false” update=”false” attributes for one of references solves the issue.

In Fluent NHibernate it looks like:

.SetAttribute(“update”, “false”);


4 comments