Hexagonal architecure – handle multiple returns from domain logic

I’m looking at using hexagonal/ports & adapters design in some code I’m working on and for the most part I’m happy but there is one part that I’m struggling with which how to handle the need for different return values from a single call.

I’ll use an example to better illustrate, this is pseudo code so names aren’t represntative of real code.

If I have a domain “service” which has a method AddEntity I can have 3 returns:

  1. Newly added object
  2. Invalid Data
  3. Entity already exists

The adapter that uses this “service” will then take the return and will send the data in the object back (it may transform it to a “view model” first).

In C# the adapter maybe a WebApi controller with code something like this:

public IHttpActionResult Post(DataViewModel entityData)
{
    var item = this.service.AddEntity(entityData.Name, entityData.Description);

    if (item == null) 
    {
        return this.StatusCode(HttpStatusCode.Conflict);
    }

    var viewModel = Mapper.Map<EntityDTO, DataViewModel>(item);
    return this.Created(this.Url.Link("DefaultApi", new { controller = "Entity", id = viewModel.Id }), viewModel);
}

The problem as you can see is at the moment it can only deal with 2 returns, either it gets the entityDTO which it maps and returns to the client or it gets null and just returns a Conflict HttpStatus code.

What I’m trying to work out is how I could get back to the adapter, controller in example above, is either the object or one of the two messages.

The approaches I’ve considered are:

  • extending the entityDTO class to have additional information which would allow me to pass something indicating a problem (an enum maybe?)
  • raising typed exceptions and handling the exceptions
  • raising events

if I used the enum then the controller code would look something like this:

public IHttpActionResult Post(DataViewModel entityData)
{
    var item = this.service.AddEntity(entityData.Name, entityData.Description);

    if(item.result == Result.Conflict)
    {
        return this.StatusCode(HttpStatusCode.Conflict);
    }

    if (item.result == Result.Invalid) 
    {
        return this.StatusCode(HttpStatusCode.Invalid);
    }

    var viewModel = Mapper.Map<EntityDTO, DataViewModel>(item);
    return this.Created(this.Url.Link("DefaultApi", new { controller = "Entity", id = viewModel.Id }), viewModel);
}

Of these extending the DTO seems the least bad option but I’m unsure if I’ve just missed something in the pattern which gives an easy way to do this.

Is there any specific way to do this I’ve missed? or is one of the options for sorting this I mentioned above a better way than the others?

Communicating errors is not part of the responsibility of a DTO, so adding a field to the DTO for that purpose should really be a last-resort option.

The typical solutions for the problem where you return either an object or one out of a set of errors are:

  • Returning a Variant record that holds either the object or the error code,
  • Returning a tuple of object and error code,
  • Returning an error code (with a special value for “no error”), and using an out-parameter for the object,
  • Returning the object and using an out-parameter for the error code,
  • Returning the object and using an exception to communicate the error.

Which option you choose depends in part on the possibilities and culture around the language you use. The option with the Variant record probably communicates the most clearly that you get either an object or an error.

There are a couple of principles to bear in mind.

The first is command-query separation here (CQS). Either you are updating, or you are querying, but not both, because this creates an issue of side-effects. Adding a new X should be a write operation, and so not return a value.

The pragmatic issue here is that you might not be able to use a Guid or Hi-lo algorithm to create the identity for the X you are about to add. In that case you need to either provide a return type, or add an out parameter to retrieve it. That should be the Id not the instance.

In this case, with a hierarchical architecture style, I might use a command pattern anyway and consider adding a property on the command that can be updated for the created id if required. That’s essentially a hack to get around an inability to create the id here. See my discussion here: http://iancooper.github.io/Paramore/CommandsCommandDispatcherandProcessor.html

Then I would use some kind of retrieve to return the instance of X if I needed it for my response i.e. a separate query. Again, I would not return the instance of X on the command or as an out parameter or return type. That minimizes your exposure to side-effects if you have to do this.

When considering error codes vs exceptions, bear in mind that an error code is effectively a query – what was the state of that operation? In many cases you could perform the command and then run the query to determine the result. For example if it is common that folks will try to recreate an existing X, query for it before the operation happens can be an alternative to throwing an exception.

Once you understand our reads and writes as all using a port layer to a model, you can appreciate calling either of them from your adapter layer.

In other cases think about the invariants you stating – the pre-conditions and the post-conditions.

  • There is not already an X.
  • X was created successfully

If you violate those conditions, I would throw an exception, to indicate that one of the invariants has failed.

1

This is actually a great question. Your proposal or Bart Van Ingen Schenau’s approaches all should work equally well. Returning tuples or a new data structure (like enum) will make your controller happy.

I guess the important thing here (and what you are asking) is how this should work in the Hexagonal Architecture. You really want to avoid returning a technical result in your ports; your ports should be technology-free. I know that the example is not from your actual system but having methods such as addEntity() is really not recommended in your ports. Similarly with return values, try returning your 3 outcomes in an enum but make sure that your hexagon names appropriately.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật