I have started using neo4j-ogm 2.1.0 and I have a basic question.
I understand that we can persist or query objects to/from a graph with a fixed, declarative domain model.
My requirement is to do this using cypher instead, so that I don't have to worry about dynamic types which may not be declared in my domain model. I have already referred to
Neo4j-Ogm for dynamic domain object model.
My question is, can I do something like:
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
String cypher = "CREATE (n:MyNode{name:"my name"}) RETURN n";
try {
session.execute(cypher);
}
If you do not care about modelling domain objects and simply want an API to execute Cypher statements from Java then you can either use the Bolt Driver (requires Neo4j 3.0 and above; refer to the manual for usage) or by using the HTTP Endpoint with a client (like this one).
If you want a mix of both then you can continue to use the Neo4j OGM and just call the query or queryForObject methods on Session to call your dynamic cypher.
Related
There are a lot of answers on how to convert ODataQuery into an Expression or into a Lambda, but what I need is quite the opposite, how to get from a Linq Expression the OData query string.
Basically what I want is to transcend the query to another service. For example, having 2 services, where your first service is not persisting anything and your second service is the one that will return the data from a database. Service1 sends the same odata request to Service2 and it can add more parameters to the original odata request to Service2
What I would like:
public IActionResult GetWeatherForecast([FromServices] IWeatherForcastService weatherForcastService)
{
//IQueryable here
var summaries = weatherForcastService.GetSummariesIQ();
var url = OdataMagicHelper.ConvertToUri(summaries);
var data = RestClient2.Get(url);
return data;
}
OP Clarified the request: generate OData query URLs from within the API itself.
Usually, the queries are so specific or simple, that it's not really necessary to try and generate OData urls from within the service, the whole point of the service configuration is to publish how the client could call anything, so it's a little bit redundant or counter-intuitive to return complex resource query URLs from within the service itself.
We can use Simple.OData.Client to build OData urls:
If the URL that we want to generate is:
{service2}/api/v1/weather_forecast?$select=Description
Then you could use Simple.OData.Client:
string service2Url = "http://localhost:11111/api/v1/";
var client = new ODataClient(service2Url);
var url = await client.For("weather_forecast")
.Select("Description")
.GetCommandTextAsync();
Background, for client-side solutions
If your OData service is a client for another OData Service, then this advice is still relevant
For full linq support you should be using OData Connected Services or Simple.OData.Client. You could roll your own, or use other derivatives of these two but why go to all that effort to re-create another wheel.
One of the main drivers for a OData Standard Compliant API is that the meta data is published in a standard format that clients can inspect and can generate consistent code and or dynamic queries to interact with the service.
How to choose:
Simple.OData.Client provides a lightweight framework for dynamically querying and submitting data to OData APIs. If you already have classes that model the structure of the API then you can use typed linq style query syntax, if you do not have a strongly typed model but you do know the structure of the API, then you can use either the untyped or dynamic expression syntax to query the API.
If you do not need full compile-time validation of your queries or you already have the classes that represent the resources served by the API then this is a simple enough interface to use.
This library is perfect for use inside your API logic if you have need of generating complex URLs in a strongly typed style of code without trying to generate a context to manage the connectivity to the server.
NOTE: Simple.OData.Client is sometimes less practical when developing against a large API that is rapidly evolving or that does not have a strict versioned route policy. If the API changes you will need to diligently refactor your code to match and will have to rely on extensive regression testing.
OData Connected Services follows a pattern where some or all of the API is modelled in the client with strongly typed client side proxy interfaces. These are POCO classes that have the structure necessary to send to and receive data from the server.
The major benefit to this method is that the POCO structures, requests and responses are validated against the schema of the API. This effectively gives you full intellisense support for the API and allows you to explor it's structure, the generated code becomes your documentation. It also gives you compile time checking and runtime safety.
The general development workflow after the API is deployed or updated is:
Download the $metadata document
Select the Operations and Types from the API that you want to model
Generate classes to represent the selected DTO Types as defined in the document, so all the inputs and outputs.
Now you can start using the code.
In VS 2022/19/17 the Connected Services interface provides a simple wizard for establishing the initial connection and for updating (or re-generating) when you need to.
The OData Connected Service or other client side proxy generation pattern suits projects under these criteria:
The API definition is relatively stable
The API definition is in a state of flux
You consume many endpoints
You don't want to manually code the types to serialize or deserialze payloads
Full disclosure, I prefer the connected service approach, but I have my own generation scripts. However if you are trying to generate OData query urls from inside your API, its not really an option, it creates a messy recursive dependency... just don't go there.
Connected services is the low-(manual)-code and lazy approach that is perfect for a stable API, generate once and never do it again. But the Connected Service architecture is perfect for a rapidly changing API because it will manage the minute changes to the classes for you, you just need to update your client side proxy classes more frequently.
I have a simple DDD service, with Article Aggregate root. I use MediatR and CQRS for separation of commands and queries. In DDD domain should not have dependencies on application and infrastructure layers. I have a repository IArticleRepository for composing some data from articles database. I have a rest endpoint for getting articles by some kind of filters so that I create
ArticleQuery : IRequest<ArticleDto(or Article)>
And when this query object should be? I have a repository per aggregate, so in Domain layer I have IArticleRepository. And I need to specify the input parameter type. If I put query in Infrastructure or Application layer I get the dependency from domain pointing to infrastructure or application. If I put query in Domain it violates DDD, because the query has no relations to business. If I will not putting an object, and just fields as a parameter to the repository, there will be about 10-15 parameters - this is a code smell.
It needed because in Query handler also appear SearchEngine logic, so I decided to encapsulate SQL logic from search engine logic in infrastructure via the repository or something like that.
I usually go for a query layer of sorts. Just as I would have an ICustomerRepository that handles my aggregates I would have an ICustomerQuery that interacts directly with my data store.
A repository really should be only for aggregates and, therefore, only for data modification. The only retrieval should be retrieving an entire aggregate in order to effect some form of change on that aggregate.
The query layer (more concern than layer, really) is infrastructure. I usually also namespace any read model in a Query namespace in order to distinguish between my domain Customer, say, and my Query.Customer.
I don't understand your question entirely, but there seems to be some confusion on how to use repositories. Answering that may help you find the right way.
Let me answer your question in two parts: where do repositories fit in, and how to use queries represent domain concepts.
Repositories are not part of the Domain layer. They belong outside in the Application layer.
A typical transaction flow would be something like this:
UI sends a request to API
API Controller gathers request params and
invokes the Application Service
Application Service gathers Repositories (Applications typically inject repositories at runtime based on configuration)
Application Service loads Aggregates (domain objects) based on the request params with the help of Repositories
Application Service invokes the methods on Aggregates to perform changes, if necessary
Application Service persists the aggregates with the help of Repositories
Application Service formats response and returns data to API Controller
So, you see, Application Service deals with repositories and aggregates. Aggregates, being in the domain layer, do not ever have to deal with Repositories.
A Query is best placed within the Repository because it is the responsibility of the Repository to interact with underlying data stores.
However, you should ensure that each Query represents a concept in the domain. It is generally not recommended to use filter params directly, because you don't capture the importance of the Query from the domain's point of view.
For example, if you are querying for, say, people who are adults (age > 21), then you should have a Query object called Adults which holds this filter within it. If you are querying for, say, people are who are senior citizens (age > 60), you should have a different Query object called Senior Citizen and so on.
For this purpose, you could use the Specification pattern to expose one GET API, but translate it into a Domain Specification Object before passing it on to the Repository for querying. You typically do this transformation in your Controller, before invoking the Application Service.
Martin Fowler and Eric Evans have published an excellent paper on using Specifications: https://martinfowler.com/apsupp/spec.pdf
As the paper states, The central idea of Specification is to separate the statement of how to match a candidate, from the candidate object that it is matched against.
Note:
Use the specification pattern for the Query side, but avoid reusing it in different contexts. Unless the Query represents the same domain concept, you should be creating a different specification object for each need. Also, DO NOT use a specification object on both the query side and command side, if you are using CQRS. You will be creating a central dependency between two parts, that NEED to be kept separate.
One way to get the underlying domain concept is to evaluate your queries (getByAandB and getByAandC) and draw out the question you are asking to the domain (For ex., ask your domain expert to describe the data she is trying to fetch).
Repository Organization:
Apologies if this confuses you a bit, but the code is in Python. But it almost reads like pseudocode, so you should be able to understand easily.
Say, we have this code structure:
application
main.py
infrastructure
repositories
user
mongo_repository.py
postgres_repository.py
...
...
domain
model
article
aggregate.py
domain_service.py
repository.py
user
...
The repository.py file under article will be an abstract repository, with important but completely empty methods. The methods represent domain concepts, but they need to implemented concretely (I think this is what you are referring to in your comments).
class ArticleRepository:
def get_all_active_articles(...):
raise NotImplementedError
def get_articles_by_followers(...):
raise NotImplementedError
def get_article_by_slug(...):
raise NotImplementedError
And in postgres_repository.py:
# import SQLAlchemy classes
...
# This class is required by the ORM used for Postgres
class Article(Base):
__tablename__ = 'articles'
id = Column(Integer, primary_key=True)
title = Column(String)
And this is a possible concrete implementation of the Factory, in the same file:
# This is the concrete repository implementation for Postgres
class ArticlePostgresRepository(ArticleRepository):
def __init__(self):
# Initialize SQL Alchemy session
self.session = Session()
def get_all_active_articles(self, ...):
return self.session.query(Article).all()
def get_article_by_slug(self, slug, ...):
return self.session.query(Article).filter(Article.slug == slug).all()
def get_articles_by_followers(self, ...):
return self.session.query(Article).filter(followee_id__in=...).all()
So in effect, the aggregate still does not know anything about the repository itself. Application services or configuration choose what kind of repository is to be used for a given environment dynamically (Maybe Postgres in Test and Mongo in Production, for example).
I have created a little project where I pass data from my controllers to a service class which uses an ORM to for example save an object:
Something like this:
The UserController receives the post data and passes it to the UserService.
The UserService creates a user object and saves it to the database with $user.save();
Now I'm struggling with two things:
First:
Let's say I use a repository to add the user, it would be like this:
Controller passes post data to the service which creates the user object and passes it to the repository. The only thing the repository has to do is call $user.save(), isn't that a bit weird? Why not calling save in my service, because using a repository just to call a save method seems overkill to me.
Second:
I read that when you use repositories, you can easily change storage methods because your application isn't aware which one is used. But before passing an object to your repository, you have to create it.
Using an ORM, each one has a different way: Doctrine uses $user = new User while Propel uses $user = new User(), idiorm uses $user = ORM::for_table('user')->create(); So when switching to another ORM for some reason comes with changing this in your project too, no?
First: Have a read about the responsibilities of Model View and Controller. There's a reasonable explanation with an example at this site: http://tomdalling.com/blog/software-design/model-view-controller-explained/
With regard to the Model and your ORM - the ORM would probably exist within the Model. So you should be asking your Model to create a new object (which may represent a single table or a series of related tables - your Model should understand these relationships). You can then pass data to your Model and your Model should then store the data into the appropriate columns in the appropriate tables. A simple example, imagine creating an object called 'Family' where you might specify 2 parent names, a variable number of children names and then tell the Model to save this. The Model may take this 'Family' object and create a single Family table record and 5 Person table records, flagging some as parents and others as children.
Second: The 'Storage Methods' referred to are (in my opinion) referring to the database you use. For example I know that Propel supports MySQL, PostgreSql, MSQL, Oracle, and others. My switch the configuration to a different database Propel will automatically start talking the appropriate language for the new database.
My server-side code runs on Dart and currently uses Redis as a data store via redis_client.
Mostly, I'm persisting primitive data types such as integers and strings. However, I also have a handful of business objects like User, and ideally I'd like to store those in Redis as well.
Of course, Redis is a key-value store and not a full-fledged document database like MongoDB. So I started writing my own serialization and persistence logic - for example, a simple getter for User:
import 'dart:convert';
...
Future<User> GetUser(String userGuid)
{
var userKey = "userGuid:" + userGuid.toString();
return redisClient.get(userKey).then((String value) {
return new User.fromJSON(value);
});
}
Before I continue hand-crafting the entire persistence logic for all my business entities - is there already some sort of Redis/Dart data access package out there I should be aware of, similar to e.g. redis_orm (Ruby)?
If not, I'll probably write my own.
(Don't wanna call it ORM because, well, Redis != relational - but effectively that's what I'm looking for)
We're using Java Hibernate + Dart at work,
I made an open source library for the client for this : https://github.com/frankpepermans/dorm
The server-side implementation is not open source however, Hibernate is not a requirement and any ORM type would do, the client communicates via JSON, so you'll need an adapter that works with JSON data and can handle/receive cyclic references (via Dorm, you can use pointers inside the JSON structure to specify a cyclic reference).
The server would also need to generate Dart class files that represent the entities.
But you can have for example :
- composite keys
- entity inheritance
- custom handling of data types (i.e. DateTime)
- custom data types (i.e. everything is Observable, lists are ObservableList by default, but you can tell the deserializer to use a custom List type if needed)
- supports annotations
I know that the session is used for the database in Hibernate, but what is the task of the session in database?
Does anyone know about this?
Update:
Apologies, my links are to Java APIs (must have missed the nhibernate tag). Regardless, there will be more than one type of session for .NET also.
There will typically be more than one type of session:
The HttpSession is a server-side object:
Provides a way to identify a user
across more than one page request or
visit to a Web site and to store
information about that user.
The hibernate Session is also a server-side object:
The lifecycle of a Session is bounded
by the beginning and end of a logical
transaction. (Long transactions might
span several database transactions.)
The main function of the Session is to
offer create, read and delete
operations for instances of mapped
entity classes.
The session is server side, if by server side you mean as in the web application or client/server application sense.
It is an implementation of the Unit of Work pattern, and does stuff like keeping track of which entities that have been changed, caching of entities and making sure that a specific entity is represented by only one actual instance in the scope of the session.
The NHibernate docs describe ISession like this:
A single-threaded, short-lived object
representing a conversation between
the application and the persistent
store. Wraps an ADO.NET connection.
Factory for ITransaction. Holds a
mandatory (first-level) cache of
persistent objects, used when
navigating the object graph or looking
up objects by identifier.