Hello and welcome to the second part of the Coding Guidelines series.
Don’t worry if you missed the first part (Naming Conventions). You can always find it here.
I have to say there is a lot of pressure for me to write this blog post mainly because “Second parts almost always suck”.
Want an example? Mission: Impossible II has the lowest IMDB score of all its series. That is valid also for Star Wars: Episode II – Attack of the Clones. And there are many other examples out there.
But then I think: the first part was the Naming Conventions, which by its nature is very formal and borderline bureaucratic. I CAN top that!
So take your seats because we are rolling the second installment of the Coding Guidelines saga.
This is not something particular to webMethods. Usage of deprecated services/methods/APIs is not recommended in any language.
There is no added value in using deprecated services because:
- when a service is deprecated, it is usually replaced by a new one, so you have other options.
- the deprecated service will be most likely removed in a future release.
You can find information about deprecated services in the “X-Y_Integration_Server_Built-In_Services_Reference” and in the release notes for a certain version.
For example in version 9.8 the service pub.publish:syncToBroker is deprecated and has been replaced by pub.publish:syncToProvider.
Public Services Try/Catch
Services that constitute the public API of a package must have a try-catch block to ensure the graceful handling of error conditions when they occur.
Try-catch can be achieved with SEQUENCE nodes by having:
- a top level sequence with the “Exit on” set to SUCCESS and
- 2 inner sequences:
- the try, with the “Exit on” set to FAILURE and
- the catch, with the “Exit on” set to DONE.
What I also like is to add comments to these sequences to clearly identify their usage.
The “finally” sequence should be used for cleanup jobs: closing streams, file descriptors, etc.
Now writing these 3 sequences can be boring at times. My workaround is to have a “template service” that has the try-catch block and copy the block when I create a new public service.
I hope that in a future release I will be able to do the following: “Right click -> New Public Flow service” and have this boilerplate code generated for me.
We will see when and if this happens.
Empty Flow services
No! No! No empty Flow services. They must contain some implementation. Otherwise, delete them.
Flow services size
Again a topic that is valid for all programming languages.
Flow services should:
- be small and modular.
- do one thing and one thing only. If a service does more things it should be split into more services.
The exception from the one thing rule is the wrapper services that call the modular services and aggregate their results.
Flow services should be small and modular also to promote reusability. A big service is less likely to be reusable and more likely to be hard to maintain.
It is a good idea to have a clear separation between the integration logic and the mapping logic. The suggestion is to have mapping services that contain only mapping logic, i.e (conditional) mapping of values from source to target with transformation if necessary.
Clearly define service signature
I find this one of the most important rules for Flow development. It may be just me because I like things clean and tidy, but seeing poorly defined service signatures is also a sign of messy code and bugs.
First rule: Carefully decide between optional and mandatory
When creating a new service take time to analyze which fields should be required and which fields should be optional.
From the consumer point of view, it is a bad practice to have all the fields required (if they do not need to be). Just because IS generates them this way is not reason enough.
This rule is even more important if the service is exposed as a Web Service Provider. In this case, if a required field is not passed, the call will fail even before the top level service is invoked.
Second rule: No hidden variables
A service should not return more variables than its contract states.
Third rule: Minimal signature
Every input variable of a service must be used within the service. If it is not used it should be deleted from the signature.
Also, do not return output variables that have no purpose. The service signature should be the minimal signature that satisfies the requirements.
Fourth rule: Use strings
The general rule of thumb when it comes to variable types is to use strings.
Integration Server works very well with strings and you should do mapping/conversion to other types:
- if it is truly necessary.
- when it is truly necessary (for example when sending the values to another system or to the DB).
Fifth rule: Maintain backward compatibility
Services should strive to be backward compatible, i.e changes to the service should not break existing clients. This means that:
- an input field should not be removed if it is used by a caller.
- if an input field is added, it should be optional.
Sixth rule: Use validation for critical services
There is a rule in programming that user input and other system input should not be trusted. This is soooo valid. Every service that is outside facing should validate the inputs it receives.
There are 2 types of validation:
- Implicit validation: The “Validate input” and “Validate output” checkboxes from the Input/Output tab
- Explicit validation: Validation that is done programmatically within the service
Explicit validation is mostly used because it gives you control over the validation logic, the error handling and error format that is thrown up the stack. It can be done against an XSD schema, against an internal data model, etc.
Seventh rule: Null is not your friend
You should not return null where an object is required as this can cause serious problems along the invocation chain.
It is better to initialize the return object at the start of the service. This way the return value would be an empty object if no data is found instead of null.
Scheduled services are services scheduled to run at certain times, to state the obvious. They can run once or can be repeating after a simple or more complex schedule. I use them for services that must run into the night: batch jobs, import jobs, prefetch jobs.
To make sure you have a smooth experience with scheduled services, the following are some proper guidelines:
- Put scheduled services in a folder called scheduler within the pub folder. This way you can easily identify which services are scheduled.
- Limit the number of input parameters of a scheduled service. It should have 0 or 1 parameters.
- Have the Audit property enabled for scheduled services to be able to track and fix problems that might appear during the service run.
- IS Scheduled services do not have very complex scheduling capabilities (i.e: no support for business calendars). A solution is to skip a service run by adding a certain condition inside the service.*
- Init the scheduled services at startup. You can check the services from the pub.scheduler folder to see how to implement the startup service. This gives the developer more control over the scheduled service.
- Use File Polling Ports if you want that your service to be triggered by the existence of a file in your import gate. Scheduled services are to be used when a service needs to run at a certain time.
*I feel this point needs further explanation. Let’s say you want a service to run on the 1st day of every month, but only if that day is not in weekend or a legal holiday.
The way you can do this is to:
- schedule the service to run on the 1st day of every month
- verify within the actual service that the current day is not weekend or holiday
- execute the service core business logic only if the condition is met
The above solution fixes the problem, but is it recommended? From my point of view, no.
I feel that it hides the information, it is a little bit misleading and makes me look in 2 places to find the correct information.