Now it's time to get an idea how we can integrate the APIs of the generated components (user-interfaces, machine-interfaces), the supported use-cases and the implemented domain classes. Since this is a rather complex issue -partially unknown to me, yet- I will try to develop this concept step-by-step in public. Any feedback is welcome.
I plan to have wxPython-based GUIs, modpython-based Web-UIs and command-line interfaces (CLI) supported by the SDK itself, but developers should be able to add other types of clients. Hence a script in Perl, a web-forum in PHP, a plug-in in VB, and so on. I guess, the CLI client will be the easiest type to start with:
Following you see a simple example of a command-line Python script foo.py. This script is implemented rather similarly to the command-line scripts of the pyswarm SDK 0.7.1. In short, it has a ScriptHandler class working as a kind of dialog-controller that is responsible for printing messages to stdout and parsing and verifying all arguments and options passed with the script at execution. Depending on the results an appropriate CommandHandler class is instantiated which is actually processing the command. (For now, let us ignore nice gimmicks like all the logging and setup features.)
OK, the ScriptHandler gets the argument bar and its value when called:
> foo.py --bar="Hello, world!"
Alternatively, bar may be just an option instead of a required parameter. So if the end-user has not provided bar, the ScriptHandler would just prompt the user to enter the string:
Error - No bar string provided.
Please enter bar: Hello, world!
For both script executions the result would be, that the CommandHandler would return the same string as it has been provided by the end-user:
You said: Hello, world!
So far it is not challenging. Though in a real project it would be insane to extract such implementation of the CommandHandler into a separate layer. We will do exactly this for a show-case, since I intent to work with more complex use-cases in later articles:
As you see, we have the actor FooUser who is assigned to the use-case uses bar. Current planning suggests that this would extend to the use-case description "FooUser uses bar" in the documentatio, that would be formatted in DocBook/XML and transformable to XHTML, PDF, etc.
The documentation for the use-case would include some instructions with steps and sub-steps, eventually also with constraints, if specific conditions need to be met. Even screen-shots of the user-interface could be part of the description. However, the source for the description would be some activity (diagram) specified as implementation of this use-case. Moreover, one step (CallOperationAction) in this activity diagram would be the actual execution of a method
bar(text:String):Stringas provided by the domain component. For now it is sufficient to know, that this method will return exactly the string we provided with the bar parameter.
The following illustration shows the implementation diagram for the given example. The script foo.py requires myAPI which is provided by myComponent. The APIs bar() method is implemented in the component's method with the same name.
For outside usage of an API it is not relevant how this method is implemented inside the component. In a simple case the component itself may do the work itself, or it will pass the call to the realizing method of a particular object. However, implementations of methods can be specified in activity diagrams and the pyswarm SDK 0.7.1 already supports fully automatic generation of convenient methods (create, update, or delete object), as well as incorporation of manually implemented source code that is taboo for the generator. Alternatively, in future pyswarm releases a model-based code-generation could utilize a given activity diagram to create the implementation in a method.
Let me try with a simplified activity diagram, although I'm not sure if I use it correctly at this place:
Execution of the foo.py script initiates the use-case which is specified by this activity. On left hand of the foo.py swimlane you see the console content for the corresponding action. Communication between foo.py and myAPI will very likely be realized with XMLRPC, but let's leave this layer for now.
Actually, I have several issues with this "pattern".
- It is rather difficult to specify the same use-case with different types of clients in use.
- I would love if the UI client could be updated by end-users, with-out root privilege. Not just only because of the extra work for each client computer, but much more to have up-to-date clients that are definitely always in compliance with the server-side.
- Another thing that confused me for too long: for use-cases there are more requirements than for simple operations. Of course, use-cases and operations may have conditional flows (if...then...) and both may get rather complex. But how will the use-case continue if it requires further action by the end-user or if an error in the domain-logic occurs? There are many more turn-outs possible than for a simple API call, like myAPI.import(data):void
For sure, there are many other issues. In my next post I want to describe, how pyswarm could deal with them. If you have any thoughts on this, please feel free to leave a comment.