|
|
|
|
|
|
Over on the Domain Driven Design discussion group Scott Bellware asked the group their opinions on how they go about driving their domain driven design implementation using test driven development. Rather then just answering him there, where only the domain driven design folks will see it, I thought it might be better to not only answer the question, but go one further and let developers into my thought process when developing an application. As luck had it, I have just started a brand new project that will eventually get released as an open source project (I actually started the code the day Scott sent out his email). So, instead of me just starting to code and eventually releasing it to the public to continue its development, I thought it might be a very good learning opportunity, on a couple different levels.
The project I have started is related to the social tagging stuff I’ve been talking about. I’m looking to create a controlled vocabulary object model built around the PRISM Controlled Vocabulary spec. The object model represents controlled vocabularies, but should also be serialized and deserialized to the PRISM CV XML format. Since PRISM’s CV spec version 1.3 relies heavily on the Resource Description Framework, simple .Net XML serialization will not work, so I’m going to have to roll my own serialization (which might also be a good thing to learn about). I’m going to use the CV spec as the user requirements for the domain driven design. Because I want to appeal to as many .Net developers as I can, I’m going to stick with .Net 1.1 and use C# as my language (but the language shouldn’t really matter).
I’m definitely not a DDD or TDD purist, nor do I think it is a good idea for anyone to just adopt a practice without adding some of their own style. I just use what works for me. It might not be 100% according to the rules, but that is fine with me. What I want developers to see is that it is alright to “bend the rules” a little and not get intimidated by purists who that think everything should be done just as the technique was written. But I’m also not infallible, so I’d also like to see some of the “experts” jump in and help correct some of things I might be doing wrong (hey, I like to learn from the experts, too). Think of this as one big code camp chalk talk session.
The take aways for this series of blog posts should be:
- Learn one way to use test driven development with Visual Studio 2003.
- Experience domain driven design in action, with Visual Studio 2003.
- Feel the pain of developing using public standards that have poorly written specs (which is the majority of them).
If you want to play along at home I’m going to take a snapshot of the solution every time I make a post. The code will be from the end of that currently development cycle. You can download the first code drop here. There isn’t much code there, but it is a pretty good starting point for this experiment. If you are new to TDD, download and installed NUnit and TestDriven.Net. If you don’t know Domain Driven Design, buy the book, and/or download the Patterns Summary. Here is how I got to this point:
- I always create a blank solution first. This project is going to be called the Controlled Vocabulary Exchange, so the solution is named CVExSolution.
- Added a C# Class Library called CVLib and set the default namespace to CvExchange, although I’m not totally happy with having the library name and the prefix namespace different. But CvExchange feels like the correct default namespace, so something is going to have to change here before I finish this project.
- Added a C# Class Library called CVLib.UnitTests which will be our test fixture for the CVLib. I then add references to NUnit Framework and the CVLib.
- In the CVLib project, delete the Class1.cs file.
- Add a folder called ControlledVocabulary. This is where the main CV domain objects will be contained.
- Add a folder called Messages. This is where I’ll put the classes that represent the message version of the domain objects (the classes that look like the XML structure).
- Looking at the spec, the classes that jump out at me are Synonym, SynonyCollection, Taxon, TaxonCollection, Vocabulary and VocabularyCollection, so I create files for them. Each one of these class will be public, but the constructors will be internal unless I find a reason to change it. I always try to start as narrow of exposure as possible.
- Taxon seems to be the hub of the domain, so I create a TaxonRepository class in the root folder, which will use the Repository pattern.
- I start to build out the Taxon class, starting with the properties. I always make my properties readonly at first and later make then read/write if need be. The Taxon class maps to the Descriptor element in the spec and represents an entry in the vocabulary. A Taxon contains collections of broader terms, narrower terms, related terms and synonyms, so I create properties for them using the collection classes. Each property has a matching private field. I add the prefix of an underscore to the property name for the field name.
- While writing the code for the narrower terms I realize that it is really a collection of taxons. Hmm, we seem to have 2 different words, taxon and terms, for the same thing. I like term better then taxon, since it seems more descriptive. I go back into the code and change Taxon to Term.
- Reviewing the comments for Descriptor I noticed that there are 2 different types of Descriptors, one that contain the actual definition and another that is just a reference to the real one (a pointer). At first I want to create an enum to handle this property, but in thinking about it, maybe we need a base class and an inheriting class (Term being the class that inherits the base class). What is the base class? Hmm, is it really just a uri? I’m not sure, so I create a class called TermLink (the link to the term) and have that inherit from System.Uri. At the moment I’m not sure if I should skip the TermLink class and just have Term inherit from Uri, but something tells me that I may need to modify the behavior of Uri, or add some additional info, so we will go with my instincts and can easily change it later if need be.
- I create a single internal constructor for TermLink that takes a string, and pass that into the base Uri constructor. For all domain objects, I always make the constructors internal. If I later decide they need to be public or protected, I open them up at that time.
- The Descriptor element contains a Label element which contains the human readable label for the term. I create a Text property in the Term class to hold this info and make it a string type.
- The Descriptor element contains a Code element which is the machine readable version of the Label element and is the unique identifier within a vocabulary. I create an Id property to hold this information. It looks like it should be a string type, so I use string, for now.
- The Definition element is also part of the Descriptor, so I need to add a property for that. I’ll keep the same name and make it a string type. I will have to change this eventually, since the spec has 2 types of definitions, one of pure text, and another that can contain markup. They recommend using XHTML, but is that a must or just a may rule. I’ll have to research that, thus, dropping back to string for now.
- Although the spec doesn’t explicitly mention carry a link to the vocabulary for each term, it seems like something that will be needed, so I add a ParentVocabulary property with a type of Vocabulary. I don’t think a term can be contained in more then one vocabulary. At least not with the exact same defintion, so I think we are good with just using a Vocabulary and not a VocabularyCollection.
- I’m done with the properties, for now, so off to take a look at the constructor. What do I need to create a complete Term. I need its Id, Text, Definition, and the parent vocabulary. Hmm, I need to pass into the base constructor a string that represents the uri. Well, the parent vocabulary should have the base Uri for the terms in the vocabulary.
- So I have to go over to the Vocabulary class and add a BaseUri property (with its matching private field). I might as well create a constructor too, for the Vocabulary, that passes in the base uri.
- Go back to the and complete the constructor by adding a constructor for the base class. I combine the string version of the parent vocabulary’s base uri property with the Id passed in to the Term’s constructor.
- I can now create a new vocabulary and also create a term for that vocabulary. But now I need to let the outside world in. So I go over to the TermRepository.
- I add a using clause for CvExchange.ControlledVocabulary and leave the default public constructor.
- Add a public method called CreateVocabulary that returns a Vocabulary type. Since a vocabulary requires a uri, this method has one parameter, a string that will become the Uri. I could have them pass in a Uri, so I might need to add an overloaded version of this method, but for now, I will leave it.
- In the new method, take the string uri and convert it to a type of Uri. Then return a new Vocabulary using the Uri. We need to add some exception handling here, but I’ll do that later, once I create some domain specific exceptions.
- Now off to create the first test.
- Delete the auto generated Class1.cs file and create our first test class, TermRepositoryTests.
- Create a test method called CreateVocabulary that will call the Repository’s method of the same name.
- Check to make sure that the return isn’t null.
That’s all the time I had to play with this new project. Wonder what will happen in the next part of this series.
Comment Notification
If you would like to receive an email when updates are made to this post, please register here
Subscribe to this post's comments using
|
|
|
|
|
About donxml
I’m an independent consultant, specializing in .Net solutions architecture, based out of New Jersey who also doubles as an evangelist for XML, Domain Driven Design, enterprise architecture and .Net. I do not work for Microsoft, the W3C or any other big company that you may know of (at least not yet). I’ve been an indie for over ten years, and although I’ve been tempted a couple times to take a job with companies like Microsoft, I’ve haven’t found something better than my current situation. I work mostly with the large pharmaceuticals that are based here in New Jersey, and usually find myself on long term contracts. Definitely not the prototypical indie consultant, but it lets me dedicate time to my non-income generating activities like the developer community stuff, plus financing open source projects like XPathmania and MVP-XML. If you would like to talk to me about doing some contract work, just contact me via the contact page. My rates vary widely, depending on lots of different variables, but mostly distance from Jersey, and type of work. Plus, I’ve been known to donate some of my code for various projects.
|
|
|