Port And Transport And Port
I use Ports-and-Adapters to abstract away my application’s interactions with external systems. I bend the dependency’s interface to the shape that I want for my domain. This makes it easier to think about my code and to unit test it.
This kind of interface might look something like:
IUserSettings Settings Get() Set(name, value) UserSettingsOnDisk : IUSerSettings Settings Get() jsonText = ReadTextFile('~/.myapp.json') return JsonDeserialize(jsonText)
Now imagine a distribute system, where I have code running on multiple computers. For example, maybe there is a security restriction that forces certain code to run on a certain computer, while some business logic runs on another computer. This is a lot like talking to an external system, except that I fully control the interface and can make it look however I want. I don’t need the same kind of adapting. But I do want to abstract away the remote communication, and I don’t want to deal with remoting when testing the business rules. If both sides are implemented using the same technology, I use this trick or organize and test them in a convenient way.
- Define the interface I wish I had. In practice, I push tweak this interface repeatedly as I push responsibilities from one side to the other.
- Make one class implement that interface, while another talks to that interface.
- Test the components together.
IFoo Bar() class ...(IFoo foo) DoWork() foo.Bar() class Foo : IFoo Bar() ... // Test manager = new Manager(new Bar()) manager.DoWork() Assert(Foo.Bar did the right thing)
- Write a transport for that interface.
- Test the transport. The question is “if I send in message X, does message X pop out on the other side?”. There’s no business logic, no conditionals to test.
class FooToHttp(IPAddress ipAddress) : IFoo Bar() HttpPost(ipAddress, "bar") class HttpToFoo(IFoo foo) ["bar"] OnPost() foo.Bar(); // Test loggingFoo = new LoggingFoo() new HttpServer(new HttpToFoo(loggingFoo)) subject = new FooToHttp(localhost) subject.Bar() Assert(loggingFoo got a call to Bar())
- In production, wire it all up together:
// Computer 1 manager = new Manager(new FooToHttp(system2ipAddress)) manager.DoWork() // Computer 2 new HttpServer(new HttpToFoo(new Foo()))
I’ve only had the chance to use this in the wild one time, so I don’t know how to triangulate it: Where is it not appropriate? What are its weaknesses? How could it be better?