Woah, Nellie.NET!

From my simple mind to… mine…

Notes from "DDD, Chapter 2: Communication and the User of Language"

leave a comment »

I’m rereading DDD by Evans for the San Antonio Tech Book Club. We’re taking it a chapter at a time. As is usual with this book, there is always more to learn every time I crack open this fairly dry, but useful tome.

The intro to Part 1 of the book defined terms such as DOMAIN, DOMAIN MODEL, and the need for the domain model. Chapter 1 discussed “Knowledge Crunching” – reworking the domain model to the essentials, changing the model based on learning, and deep models.

Chapter 2 is about the use of the UBIQUITOUS LANGUAGE (UL).

  • If the DOMAIN MODEL is the refined elements of the domain, as decided upon by business experts and developers, then the UBIQUITOUS LANGUAGE (UL) is the common language that is more robust than – but still based on – the model.
  • Schisms should not be allowed when using the UL. That is, different team members should not use terms in different ways from the agreed upon meaning. If so, a change in the model is required.
  • With no UL in place, or if schisms are allowed, then the need to translate between different uses of terms is significant. When the terms used in the code are different from those used in day-to-day language, then other aspects of modeling become troublesome (“makes knowledge crunching anemic”).
  • The UL consists of names of classes and methods, rules, organizing principles, model relationships.
  • The UL should be used by all team members to make it pervasive and thus ubiquitous. Team must be committed to this.
  • Model out loud. Use the UL in day-to-day speech.
  • Although the UL can consist of more than what is in the code, when something is identified as strange in the UL, then that concept/term should be corrected and propagated back to the DOMAIN MODEL.
  • The model derives from the domain expert’s language, but is refined. Domain experts should object if the meaning of something in the model diverges from the meaning accepted in the domain.
  • As with anything Agile, the UL should be constantly reworked as concepts are introduced or changed.
  • Multiple languages in the domain are allowed, but NEVER between the domain expert and developer. A language EXTENSION  is specialized jargon that is not used in the UL but has meaning to a certain party (developer or domain expert) to discuss an aspect of the system. See Figure 2.3 in the book.
  • A few pages of discussion on documentation. My take is to realize that 1) the UL will always be more dynamic than documentation, 2) you must be careful as to what concepts you decide to commit to paper since the moment you do, you have to maintain that documentation especially if the UL is always changing, and 3) the model is NOT just the diagram representation – any documentation beyond the code should complement the model. All other details should be captured in the code since it is the definitive representation of the agreed upon model.
  • Code must be based on the UL and DOMAIN MODEL to be useful. Developer should not say something in the UL and represent that concept differently in the code.
  • EXPLANATORY MODELS are models that are not related to code, but can be used for learning. They are often a different representation of the DOMAIN MODEL or EXTENSIONS.
  • BOTTOM LINE: The UBIQUITOUS LANGUAGE is important because it goes beyond a purposefully limited DOMAIN MODEL and acknowledges that the spoken language is more dynamic than code, diagrams and other forms of documentation.
Advertisements

Written by Nelson

December 1, 2008 at 9:21 am

Incorporating Liquibase into NAnt Builds

leave a comment »

With Liquibase for .NET a little ways off, I spent a little time trying to get Liquibase for Java incorporated into my NAnt build. The following lessons learned were originally from a spike, but I’ve gone ahead and incorporated liquibase into my new project. Let me know if you have something better.

The first step is to download the Liquibase jar, the SQL Server JDBC driver (if you are using SQL Server) and ikvm.

Liquibase is a pretty good database refactoring tool, facilitating such things as multiple DB support, multiple developer merges, test data inserts for unit testing, and so on.

ikvm is a Java implementation for .NET and Mono. I chose this route as opposed to installing Java just to keep things easy for me.

My NAnt target utilizes the <exec> task to execute liquibase using ikvm.exe:

<target
  description="Sets up database for unit testing purposes"
  name="liquibase.for.testing">
    <exec basedir="..\..\tools\ikvm"
      managed="true"
      program="ikvm.exe">
        <arg line="-jar ..\..\tools\liquibase\liquibase-1.8.1.jar" />
        <arg value="--logLevel=SEVERE" />
        <arg value="--classpath=..\..\tools\ikvm\sqljdbc.jar" />
        <arg value="--driver=com.microsoft.sqlserver.jdbc.SQLServerDriver" />
        <arg value="--url=jdbc:sqlserver://localhost;databasename=DirectSalesSoftware;integratedSecurity=true;" />
        <arg value="--changeLogFile=.\Persistence\liquibase\db.changelog.xml " />
        <arg value="--contexts=tests" />
        <arg value="update" />
    </exec>
  </target>

From here, I setup a batch file that I can double click to update my database:

@ECHO OFF
@..\..\tools\nant\bin\nant.exe -buildfile:default.build liquibase.for.testing
PAUSE

Written by Nelson

November 7, 2008 at 9:14 am

Posted in .NET, Agile Software

Productivity 2.0 – sounds a lot like Agile

leave a comment »

I was reading through recent ZenHabits posts in my RSS reader and came upon this post. A lot of the points made seem to be a part of Agile based methodologies.

Written by Nelson

October 14, 2008 at 5:32 am

Rhino Mocks’ new Arrange, Act, Assert model…

leave a comment »

The difference between mocks and stubs in Rhino Mock’s new triple-A model is so subtle, that I can’t really distinguish between the two (except in ordering scenarios, apparently).

Oh well… 🙂

I love the syntax. In my test:

        
        [TestFixture]
        public class When_creating_a_replication_context_based_on_information_in_the_session
        {
            #region Setup/Teardown

            [SetUp]
            public void Setup_context()
            {
                session = MockRepository.GenerateStub<HttpSessionStateBase>();
                session.Stub(x => x["replicatedSite"]).Return("bar");

                HttpRequestBase request = MockRepository.GenerateStub<HttpRequestBase>();
                request.Stub(x => x["replicatedSite"]).Return(null);

                HttpContextBase context = MockRepository.GenerateStub<HttpContextBase>();
                context.Stub(x => x.Session).Return(session);
                context.Stub(x => x.Request).Return(request);

                context.Stub(x => x.Session).Return(session);
                session.Stub(x => x.Add("replicatedSite", "bar"));

                replicationContextFactory = new HttpParamsReplicationContextFactory(context);
                replicationContext = replicationContextFactory.Create();
            }

            #endregion

            private HttpParamsReplicationContextFactory replicationContextFactory;
            private ReplicationContext replicationContext;
            private HttpSessionStateBase session;

            [Test]
            public void Should_return_instance_of_subdomain_replication_context()
            {
                Assert.That(replicationContext.GetType().ToString(), Is.EqualTo("LinearJunction.Replication.Domain.SimpleReplicationContext"));
            }

            [Test]
            public void Should_return_replicated_site()
            {
                Assert.That(replicationContext.ReplicatedSite, Is.EqualTo("bar"));
            }

            [Test]
            public void Should_set_NEW_replicated_site_into_session()
            {
                session.AssertWasCalled(x => x.Add("replicatedSite", "bar"));
            }
        }

Written by Nelson

October 8, 2008 at 2:46 am

Posted in .NET

Cross Loop

leave a comment »

Interesting way to help people out: Cross Loop

Watch tv while you wait: Hulu

Written by Nelson

September 25, 2008 at 7:46 am

Posted in Miscellaneous

ASP.NET MVC Model Binders

leave a comment »

Model binders are a great way to allow the ASP.NET MVC framework to bind the passed request or query string data to a model object.

For more information start here with Scott Gu’s blog:

http://weblogs.asp.net/scottgu/archive/2008/09/02/asp-net-mvc-preview-5-and-form-posting-scenarios.aspx

I found this… the fourth post down by freechoice being most significant:

http://forums.asp.net/p/1317611/2611395.aspx

There is code in there for a dynamic model binder, that is a great solution.

I threw together a quick test and sample model binder just to spike out my options:

#region

using System.Collections.Specialized;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using LinearJunction.Leads.Domain;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
using Rhino.Mocks;

#endregion

namespace LinearJunction.Leads.Web.Specs
{
    public class LeadModelBinderSpecs
    {
        #region Nested type: When_lead_form_data_is_submitted

        [TestFixture]
        public class When_lead_form_data_is_submitted
        {
            #region Setup/Teardown

            [SetUp]
            public void Setup_Context()
            {
                SetupMocks();
                SetupExpectedRequestData();
                SetupExpectations();

                ControllerContext controllerContext = new ControllerContext(httpContextBase, new RouteData(), controllerBase);
                boundLead = new LeadModelBinder().GetValue(controllerContext, "Lead", typeof (Lead), modelStateDictionary);
            }

            #endregion

            private void SetupExpectations()
            {
                httpContextBase.Expect(context => context.Request).Return(requestBase).Repeat.Times(4);
                requestBase.Expect(request => request.QueryString).Return(null).Repeat.Times(4);
                requestBase.Expect(request => request.Form).Return(nameValueCollection).Repeat.Times(12);

                mockRepository.ReplayAll();
            }

            private void SetupMocks()
            {
                mockRepository = new MockRepository();
                controllerBase = mockRepository.DynamicMock<ControllerBase>();
                httpContextBase = mockRepository.StrictMock<HttpContextBase>();
                modelStateDictionary = mockRepository.PartialMock<ModelStateDictionary>();
                requestBase = mockRepository.StrictMock<HttpRequestBase>();
            }

            private void SetupExpectedRequestData()
            {
                nameValueCollection = new NameValueCollection();
                nameValueCollection.Add("FirstName", "Joe");
                nameValueCollection.Add("LastName", "Bob");
                nameValueCollection.Add("EmailAddress", "foo@nowhere.com");
                nameValueCollection.Add("PhoneNumber", "2102221234");
            }

            private object boundLead;
            private ModelStateDictionary modelStateDictionary;
            private NameValueCollection nameValueCollection;
            private ControllerBase controllerBase;
            private HttpRequestBase requestBase;
            private HttpContextBase httpContextBase;
            private MockRepository mockRepository;

            [Test]
            public void Should_return_new_lead()
            {
                Assert.That(boundLead, Is.Not.Null);
                Assert.That(boundLead, Is.InstanceOfType(typeof (Lead)));
            }

            [Test]
            public void Should_have_lead_data_set()
            {
                Lead lead = boundLead as Lead;
                Assert.That(lead.EmailAddress, Is.EqualTo("foo@nowhere.com"));
                Assert.That(lead.FirstName, Is.EqualTo("Joe"));
                Assert.That(lead.LastName, Is.EqualTo("Bob"));
                Assert.That(lead.PhoneNumber, Is.EqualTo("2102221234"));
                Assert.That(lead.LeadStatus, Is.EqualTo(LeadStatus.NEW));
            }
        }

        #endregion
    }
}

And the resulting class:

#region

using System;
using System.Web.Mvc;
using LinearJunction.Leads.Domain;

#endregion

namespace LinearJunction.Leads.Web
{
    public class LeadModelBinder : DefaultModelBinder
    {
        public override object GetValue(ControllerContext controllerContext, string modelName, Type modelType, ModelStateDictionary modelState)
        {
            string firstName = (string) base.GetValue(controllerContext, "FirstName", typeof(string), modelState);
            string lastName = (string)base.GetValue(controllerContext, "LastName", typeof(string), modelState);
            string emailAddress = (string)base.GetValue(controllerContext, "EmailAddress", typeof(string), modelState);
            string phoneNumber = (string)base.GetValue(controllerContext, "PhoneNumber", typeof(string), modelState);

            return new LeadFactory().CreateNewFrom(firstName, lastName, emailAddress, phoneNumber);
        }
    }
}

I’ll go ahead and register it on application startup.


        protected void Application_Start(object sender, EventArgs e)
        {
            ModelBinders.Binders[typeof (Lead)] = new LeadModelBinder();

            RegisterRoutes(RouteTable.Routes);
            InitializeWindsor();
        }

Finally, note that I used this approach because of my use of a factory to create the new lead. It’s a great way to handle a significant aspect. If you have no additional requirements outside of binding form data to your model object, you can use the complex binder provided by the MVC framework.

Now, something like this should just work, as the form data will be bound to the Lead instance and passed into the controller’s Submit method:


#region

using System.Web.Mvc;
using LinearJunction.Leads.Domain;

#endregion

namespace tbdnetwork.Web.LeadCapture.Controllers
{
    public class LeadController : Controller
    {
        private readonly LeadRepository leadRepository;

        public LeadController(LeadRepository leadRepository)
        {
            this.leadRepository = leadRepository;
        }

        [AcceptVerbs("POST")]
        public ActionResult Submit(Lead lead)
        {
            leadRepository.Save(ref lead);
            return RedirectToAction("SubmissionStatus");
        }
    }
}

Let me go test it and find out! 😉

Written by Nelson

September 24, 2008 at 6:20 am

Posted in .NET

Slingplayer

leave a comment »

Frustrated with the HD support for most TV cards and Windows Home Media Server, I gave up and bought a Slingplayer.

In addition, remote control support for my AT&T U-Verse cable box through Windows is non-existent as far as I can tell. The Slingplayer supports this well.

So far, so good AND I can watch it from anywhere I can access my network! Seems like there might be an iPhone version of the client software soon. We’ll see…

Written by Nelson

September 24, 2008 at 2:57 am

Posted in Hardware