Liferay's Architecture: The services layer

Here I am again for a second entry on the series of Liferay Architecture. This time I'm going to talk a bit more about the services layer of Liferay. As can be seen in the architecture diagram with which I started this series, the services layer is at the core of Liferay Portal:

The Services layer contains the great majority of the business logic for the portal platform and all of the portlets included out of the box. The services are organized in two sets:

  • Portal services (com.liferay.portal.service): contains the services for the portal level entities. A simple trick to identify them is to look at the "Portal" section of the Control Panel menu.
  • Portlet services (com.liferay.portlet.*.service): contains the services used by the different portlets in Liferay. There are also some packages that are not really associated to one portlet (or to more that one), so the word portlet here is being abused a little bit. What matters is that all the services that are no part of the core portal platform are properly organized and componentized.

One very specific aspect of the services layer is that it is divided in two sub-layers:

  1. Local Services (<Entity> this are the ones that that contain the business logic and communicate with the persistance layer.
  2. Remote Services (<Entity> the main goal is to perform security checks before invoking the equivalent method in the local service. In some cases the remote services are also responsible for converting the Java objects returned by the local services layer to other formats. For example, the RSS generation for portlets that support it is done in remote services.

I'm going to finish the entry with some further facts and patterns of Liferay's services layer. The first are more well known but some aren't:

  1. Each persisted entity has an associated service. For example, the User entity has UserService, DLFileEntry (the entity used to store documents of Documents & Media) has the DLFileEntryService.
  2. The services methods are executed in a transaction. That means that if your database supports it (and most do) if there is an error in the middle of the execution of a service method (or in any other method invoked by it), all the previous operations will be undone (rolled back) automatically for you. Liferay implements this under the hood using Spring's transations mechanisms.
  3. The persistance layer is always accessed from local services. You should never access it directly unless you really know what you are doing and you are able to handle transactions manually from the invoking code.
  4. The local services are very strict with the return types of its methods. The return type should be one of the following:
    1. void
    2. <Entity>
    3. List<Entity>
    4. primitive type (this is not used often)

That's it for this second entry. I'm looking forward for your feedback. I'd love to know what topics are more interesting to you so that I can keep them into account for next entries.

Update Nov 29th: Added primitive type as a 4th return type. Thanks David for the note about it.

"DLFileEntry (the entity used to store documents of Documents & Media) has the DLFileEntry"

Service is missing at the end I guess.

Thanks for the useful article again!
Amazing post!
Finally we have a great overview of the LR Architecture in one single place. I mean: your presentation of LR Architecture in the European Symposium with these blog posts :-).
It would be great it this topics were also integrated in the LR Development Guide:
This would help a lot new developers (I know that it would have helped me a lot 2 years ago when I started with LR :-).
Thanks Remis!
That's exactly my plan. Once I finish the blog series I'll work with Jim Hinkey, the main author of the Dev Guide to incorporate the content in the next version.
If you are looking for suggestions for next one I think an explanation of the URL Filtering system and how/ the order of filters is applied, which ones can safely be disabled for performance and which ones never should be would be useful if it isn't covered elsewhere of course.
Thank you for these posts. They definitely help structure.

Could you please explain why the return types of the local services is so strict? What are the consequences of deviating from this in user defined services ?
Well, there is a 4th type, a primitive (or the object equivalents).

The reason for the strictness is the CLP layer. ServiceBuilder creates a shim layer so calls are successful over the class loader boundary between different web applications (your plugins are separate wars and have their own class loader).

ServiceBuilder, however, only knows about the types defined in the service.xml file. It has no visibility over other types, no idea if they are appropriately serializable, etc.

There are two ways to use non-DB based entities as return types:

1) the return types must be defined at a global scope (put your classes in a jar and push the jar to the app container's global library, i.e. lib/ext in Tomcat).

2) There's a suggestion here that would seem to allow for a regular entity definition that might not actually get persisted (as long as you didn't invoke one of the standard CRUD methods).
@Dave Thanks for the suggestion, I do have some slides that cover URL handling but that will be several posts from now emoticon

@Alain, what I'm describing are the conventions we follow at Liferay. We follow them because we think having strict conventions makes it significantly easier to maintain the code for a product this large. Additionally, as David says, these conventions can also be leveraged by our own tools such as Service Builder to do some work for us. You don't need to follow the same conventions (I guess most people won't), but if you use Service Builder you will get benefits from following some of them.

@David, good catch on the 4th type, I'll add it to the post
Thanks David. Certainly helps a lot.

I had initially understood the restriction as being "the entity for which the service is defined". If I understand your answer correctly, I should read the restriction as "the entities defined by ServiceBuilder, i.e. DB based entities".

Not yet clear to me however if the restriction refers to entities in THE currently processed service.xml or if it extends to portal entities (Users, Organizations, ...) and entities defined in service.xml in other wars.
As I can read in development documentation, Liferay web services API support only basic authentication. Is it possible to enforce authentication policy? Thanks in advance
Hi Andrea,
If you are accessing the web services API through an unsecure channel we recommend you to use HTTPS to protect your credentials.
For next version we are also adding Oauth as an alternative authentication option for web services.
@Alain, other entities are supported within the same class loader using the @reference object.... But the limitation here is the 'same class loader' thing. Your entities are in one class loader, the portal's entities are in the portal's class loader, and other SB-based plugins will be in their own class loaders...

Here you're kind of stuck returning PKs for the entities that need to be pulled in and use those PKs to fetch them manually.
Hi Jorge,
which Oauth versione (1.0 or 2.0) will be supported as option for web service authentication?
Thanks in advance,
Right now I think it's 1.0.

Oauth 2.0 is more of a framework than a protocol.
Hi Jorge ,
Nice explanation of the liferay service architecture. But service methods are also executed using <entity>LocalServiceClp. Would you please also elaborate the motive of <entity>LocalServiceClp generated by service builder.

Hi Jorge ,
Nice explanation of the liferay service architecture. But service methods are also executed using <entity>LocalServiceClp. Would you please also elaborate the motive of <entity>LocalServiceClp generated by service builder.

Hi Sushil,

<entity>LocalServiceClp can be used to invoke services defined and implemented in one plugin from another plugin. This class is autogenerated to handle the classloading manipulation necessary to do this, since the default classloading mechanisms of J2EE does not allow for it.

Another way of achieving the same and one in which we are investing for 6.2 and future versions is using OSGi.
Hi Jorge,
Thanks for quick response. Whether it is possible to use OSGI in earlier versions(LR6 ,LR6.1) also ? .
Liferay's Module Framework, which allows developing OSGi modules to extend Liferay, is only available since 6.2. I guess nothing prevents you from using OSGi on your own, but I guess it would be much more work.