The major components of ILE are activation groups and service programs. We've addressed activation groups; now it's time to get practical with service programs.
At its most basic level, a service program provides an application program with a set of related procedures that group together like functions. Typically, these functions rely on one another and may even encapsulate shared data, providing some of the more powerful and useful features of object-oriented programs to good old procedural ILE programs. In order to use service programs, you need to consider three steps: grouping your procedures, including your prototypes, and binding your programs. Each of these steps is critical to the process, and this series of articles will familiarize you with each of the steps. In additional tips and articles, I'll identify specific techniques that you can use to make your job easier.
Beginning Your Grouping
One of the more important decisions in your application infrastructure will be how you design your external procedures and gather them into service programs. As you begin your trek into the world of service programs, the first step is to start to think modularly. By that, I mean thinking of your business logic as small, discrete pieces of code. Java programmers do this all the time, but it's not always the way we RPG programmers think. The changes in the language over the last 10 years have certainly made it easier, but changes in the language don't always translate to changes in our thinking. But let me guide you through how you might get started. Let's start with an area that can commonly be served with a good routine: date processing. While the RPG language has progressed dramatically in its date-handling capabilities, even the best conventional date-handling needs some help when it comes to business logic. It's great that we can directly subtract two dates, but even the best languages lack intrinsic features for business concepts like prox dates.
A quick business definition: prox terms are used to set the due date for an invoice at a specific day of the month. As an example, "prox 20" means due on the 20th of the next month. There are some nuances; some companies will force the due date out an additional month if the date is near the end of the current month. That ensures that the due date is at least a month out. Picture instead if an invoice was due prox 20 but today was the 30th; the due date would be the 20th of next month or only about 20 days away.
Let's start there. Let's assume we want to create a simple date service program that can calculate prox dates. In that service program, we might have a handful of routines:
CredCalcDates( currentDate: netDays: discDays: proxDay: dueDate: discDate);
CredGetProx( currentDate: proxDay);
DateSetDay( currentDate: dayNumber);
The exact code for these routines isn't important, but it is critical to understand how and why they are grouped together into this service program. The first routine, CredCalcDates, is the primary routine that is used by business applications. Because it needs to return multiple values, it's written as a traditional RPG procedure with both input and output parameters (as opposed to more function-like procedures, which have only input parameters and return a single value via the return opcode). You could also return a data structure with multiple values in it, and that makes sense in certain cases. But for this particular example, I'm using the input/output parameter technique.
Let me pause here for a quick segue into the issue of procedure naming. That could be an entire article in itself, and I may expand on it later. For today's purposes, though, I'll simply briefly explain the names I've chosen. My convention is to use multi-word procedure names with the first word/abbreviation identifying the application. In this case, the prefix Cred indicates a procedure that is part of the Credit services. Why Cred and not Credit? Because the fact that I can use incredibly long names doesn't necessarily mean I should. I find that very, very long names make my code unwieldy and my programs less rather than more readable. I don't mind long names, but I prefer to use Acct to Accounting and Mfg to Manufacturing. In the end, I try to stick to application abbreviations of four or fewer characters.
Another convention I like to use is that if the second word is Calc, the routine has input and output parameters, while a second word of Get signifies a function that returns a result. I know it seems a bit nit-picky, but as your library of service programs grows you'll really appreciate good naming conventions. In any case, the CredCalcDates routine takes a few parameters: the current date, the net days, the discount days, and the prox day. Using those, it updates two dates: the due date and the discount date. Again, the exact working of the function isn't important; it's simply an example. What is important is that this routine has to calculate prox dates, and in order to do that it relies on a second procedure.
And that brings us to CredGetProx. Based on the previous discussion, you should already be able to guess that this routine is part of the Credit services and that it takes one or more input-only parameters and returns a value. In this case, the routine takes the specified date and then calculates the prox date based on the day of the month specified. Remember that you calculate a prox date by using the specified day number in the following month (or under certain circumstances, the month after that). So CredCalcDates will invoke CredGetProx, but if any other program needs a prox date, it can also call CredGetProx. Is this strictly necessary? Perhaps not. If at the end of the day CredCalcDates is the only procedure that calls CredGetProx, then you have a reasonable argument for combining the code. But I tend to err on the side of more modularization rather than less.
Finally, RPG provides a rich set of built-in functions (BIFs) for date-handling but not everything I need for prox calculations; I still needed one additional utility routine. DateSetDay provides a function that just isn't available, changing the day of a date. It's not particularly difficult, but you do have to handle interesting situations like trying to set February 30th. So we write our own procedure to handle it and put it in a service program. Next, we have a couple of application-specific routines that actually deal with due dates. You might wonder why I didn't put this routine into the Credit application. It's because I consider prox calculations to be a business function, but I consider creating a date for a specific day of the month to be a utility function that could be used by any application. This is a somewhat arbitrary decision, and it's one of those areas where programming is still a bit of an art. An argument could be made that prox dates might cross applications, but we'll deal with that in a later discussion. For now, I hope you've just gotten an idea of the sort of modularization you need to use to begin arranging your business logic into services programs. Next, we'll start working on making that code available to your application programs through prototypes and binding directories.