Clean Architecture – How to Adapt Clean Architecture to a Rich Desktop Application

clean-architecturedesigndomain-driven-design

I'm just learning about clean architecture and I'm trying to design a proof of concept for an application I want to build soon.

In the Clean Architecture the Presentation layer and the Domain Model layer are separated by the Application layer. I understand how this make sense for stateless applications, like a Web base MVC application or a "record based" desktop application where most operations are CRUD. Also, my understanding is that the Presentation layer should not directly use the domain object, but should have it's own ViewModel that are mapped to/from models from the application layer. Is this assumption correct?

The application I plan to develop will allow the user to input chess games one move at a time. It seems to me that the rules for chess should be in the Domain Model layer. If this is the case, how does the Presentation layer validate each user input (to verify each moves legality)? Does it need to go through the Application Layer every time or does it makes more sense to let the Presentation Layer somehow manipulate the Domain objects directly to build the model according to the user input and then send it to the Application layer when it's time to save it to the database?

I tried to find resources online that would talk about this, but it seems all example/course/tutorial I found talk about a web application or at least a stateless application of the CRUD type where the business rules are applied once before saving the data or after loading them. In my application the chess rules needs to be applied every time the user edit the ViewModel to give an immediate feedback.

(As I wrote this, the rubber duck effect kicked in and I now think that maybe I should always go through the application layer. I would still like to know what more experienced people think)

Best Answer

I am using Clean Architecture for a rich desktop application since about a year now and it works well for GUI-heavy, event-based applications. It's not a good fit for a "real-time" game like a jump'n'run, but a chess game will work just fine.

Instead of dissecting your question, I will go through a recap of the layers and will end with following the process of moving a figurine.

The Entities Layer

"Enterprise wide business rules". In your case, that means encapsulating the rules of the game. Having the age old game of chess here instead of an actual business gives you an advantage - once you wrote this layer, the only way it will ever need a change is if you found a bug here.

The Use Cases Layer

Think of use cases as application user actions. In this layer, you define these user actions, the input they take, the output they give back and the interfaces they use in order to achieve their goal. Such a use case will not really do any work itself. It's more like a step-by-step description of what happens.

The Interface Adapters Layer

The code you write that does the "grunt work" goes in here.

The Framework & Drivers Layer/Details Layer

This layer is purely an adapter from some external thing to your code (= the inner 3 layers). It consists almost completely of wrappers. This part needs to be so thin/simple that it doesn't need to know about the other 3 layers. It doesn't know about your models, it only works with strings, ints, events, etc.

The exception to this might be your setup code that connects everything, which is also in this layer for practical reasons rather than semantical.

(You called this the "Presentation layer", but that's a bit misleading. On one hand because a DB, some hardware device, internet access etc are also all external things that get their wrappers in this layer. On the other hand because all the "view logic" - like turning a model into strings for the GUI - happens in the previous layer.)

Moving a chess piece

Details layer

  • In the GUI, the user drags a chess piece to a different square
  • The class listening to that happening is a BoardView in the details layer
  • The BoardView calls a BoardViewPresenter with the simple info ('A', 1, 'B', 3)

Interface Adapters layer

  • The BoardViewPresenter translates that into proper models from your Entities layer.
  • It finds the ChessPiece on A1 and the BoardPosition B3.
  • It puts those into a MoveChessPieceRequest from the use case layer.
  • It sends that to the MoveChessPieceUseCase (obviously also from the use case layer).

Use Case layer

  • The MoveChessPieceUseCase validates the request according to the rules from the Entities layer.
  • It goes through the steps "The target square is free", "The type of chess piece can move like that" and "There's no check to resolve".
  • If any of the validation steps would have failed, the use case returns early a MoveChessPieceResult with a boolean Succeeded set to false.
  • However, in this example the request is valid, so it changes the Entities in memory and returns a positive result.

Interface Adapter Layer

  • The BoardViewPresenter now has the MoveChessPieceResult and reacts to the positive result.
  • It gets the ChessPieceView and tells it Move('B', 3).

Details layer

  • The ChessPieceView changes its actual position on the screen.
  • It also plays a "move" sound