- The functional units are already independent or loosely coupled in the target domain (from the conceptual perspective).
- The functional requirements allow asynchronous communication between the components. Even better: they are already modeled as asynchronous processes.
- The service component, which offers the services, is likely to change, what would break the "client" component.
- The service component is not technology or even vendor agnostic. Direct communication with such service component would "pollute" all clients. Encapsulation is a must in this case.
- Both components have different release cycles. Service component is likely to change, but the client component cannot be re-installed or rolled-out.
- Both components are going to be developed by different teams, or even some parts outsourced to other companies.
- An average RFE will cause only local changes in a single component and the decoupling / indirection will help you to isolate those changes.
- There are already different algorithms available for a given functionality or use case (see e.g. the Strategy Pattern).
- You are building an API or platform, which is going to be used by other applications.
- Obvious: the UI should be always decoupled from the business logic realization. Multiple UIs (e.g. JSF, RIA, IPhone) are very likely - also in enterprise environment.
Decoupling or modularization do cause additional coding / conceptual / auditing (e.g. metrics) effort. The reason / intention for decoupling or modularization has to be clearly documented, otherwise the resulting code can be hard to understand and, in long term, to maintain.
[Data Transfer Objects are often introduced to increase decoupling, see page 153 in "Real World Java EE Patterns - Rethinking Best Practices" and Premature Encapsulation Is the Root of All Evil, Page 253]