How to structure a RESTful backend API with a database? - rest

I want to make an API using REST which interacts (stores) data in a database.
While I was reading some design patterns and I came across remote facade, and the book I was reading mentions that the role of this facade is to translate the course grained methods from the remote calls into fine grained local calls, and that it should not have any extra logic. As an explaination, it says that the program should still work without this facade.
Here's an example
Yet I have two questions:
Considering I also have a database, does it make sense to split the general call into specific calls for each attribute? Doesn't it make more sense to just have a general "get data" method that runs one query against the database and converts it into an usable object, to reduce the number of database calls? So instead of splitting the get address to get street, get city, get zip, make on db call for all that info.
With all this in mind, and, in my case using golang, how should the project be structured in terms of files and functions?
I will have the main file with all the endpoints from the REST API, calling the controllers that handle these requests.
I will have a set of files that define those controllers. Are these controllers the remote facade? Should those methods not have logic in that case, and just call the equivalent local methods?
Should the local methods call the database directly, or should they use some sort of helper class that accesses the database?
Assuming all questions are positive, does the following structure make sense?
Main
Controllers
Domain
Database helper

First and foremost, as Mike Amundsen has stated
Your data model is not your object model is not your resource model is not your affordance model
Jim Webber did say something very similar, that by implementing a REST architecture you have an integration model, in the form of the Web, which is governed by HTTP and the other being the domain model. Resources adept and project your domain model to the world, though there is no 1:1 mapping between the data in your database and the representations you send out. A typical REST system does have many more resources than you have DB entries in your domain model.
With that being said, it is hard to give concrete advice on how you should structure your project, especially in terms of a certain framework you want to use. In regards to Robert "Uncle Bob" C. Martin on looking at the code structure, it should tell you something about the intent of the application and not about the framework¹ you use. According to him Architecture is about intent. Though what you usually see is the default-structure imposed by a framework such as Maven, Ruby on Rails, ... For golang you should probably read through certain documentation or blogs which might or might not give you some ideas.
In terms of accessing the database you might either try to follow a micro-service architecture where each service maintains their own database or you attempt something like a distributed monolith that acts as one cohesive system and shares the database among all its parts. In case you scale to the broad and a couple of parallel services consume data, i.e. in case of a message broker, you might need a distributed lock and/or queue to guarantee that the data is not consumed by multiple instances at the same time.
What you should do, however, is design your data layer in a way that it does scale well. What many developers often forget or underestimate is the benefit they can gain from caching. Links are basically used on the Web to reference from one resource to an other and giving the relation some semantic context by the utilization of well-defined link-relation names. Link relations also allow a server to control its own namespace and change URIs as needed. But URIs are not only pointers to a resource a client can invoke but also keys for a cache. Caching can take place on multiple locations. On the server side to avoid costly calculations or look ups on the client side to avoid sending requests out in general or on intermediary hops which allow to take away pressure from heavily requested servers. Fielding made caching even a constraint that needs to be respected.
In regards to what attributes you should create queries for is totally dependent on the use case you attempt to depict. In case of the address example given it does make sense to return the address information all at once as the street or zip code is rarely queried on its own. If the address is part of some user or employee data it is more vague whether to return that information as part of the user or employee data or just as a link that should be queried on its own as part of a further request. What you return may also depend on the capabilities of the media-type client and your service agree upon (content-type negotiation).
If you implement something like a grouping for i.e. some football players and certain categories they belong to, such as their teams and whether they are offense or defense players, you might have a Team A resource that includes all of the players as embedded data. Within the DB you could have either an own table for teams and references to the respective player or the team could just be a column in the player table. We don't know and a client usually doesn't bother as well. From a design perspective you should however be aware of the benefits and consequences of including all the players at the same time in regards to providing links to the respective player or using a mixed approach of presenting some base data and a link to learn further details.
The latter approach is probably the most sensible way as this gives a client enough information to determine whether more detailed data is needed or not. If needed a simple GET request to the provided URI is enough, which might be served by a cache and thus never reach the actual server at all. The first approach has for sure the disadvantage that it doesn't reuse caching optimally and may return way more data then actually needed. The approach to include links only may not provide enough information forcing the client to perform a follow-up request to learn data about the team member. But as mentioned before, you as the service designer decide which URIs or queries are returned to the client and thus can design your system and data model accordingly.
In general what you do in a REST architecture is providing a client with choices. It is good practice to design the overall interaction flow as a state machine which is traversed through receiving requests and returning responses. As REST uses the same interaction model as the Web, it probably feels more natural to design the whole system as if you'd implement it for the Web and then apply the design to your REST system.
Whether controllers should contain business logic or not is primarily an opinionated question. As Jim Webber correctly stated, HTTP, which is the de-facto transport layer of REST, is an
application protocol whose application domain is the transfer of documents over a network. That is what HTTP does. It moves documents around. ... HTTP is an application protocol, but it is NOT YOUR application protocol.
He further points out that you have to narrow HTTP into a domain application protocol and trigger business activities as a side-effect of moving documents around the network. So, it's the side-effect of moving documents over the network that triggers your business logic. There is no straight rule whether to include business logic in your controller or not, but usually you try to keep the business logic in yet their own layer, i.e. as a service that you just invoke from within the controller. That allows to test the business logic without the need of the controller and thus without the need of a real HTTP request.
While this answer can't provide more detailed information, partly due to the broad nature of the question itself, I hope I could shed some light in what areas you should put in some thoughts and that your data model is not necessarily your resource or affordance model.

Related

REST API Design - adapter service - how to mark endpoints for different directions?

Let's imagine a web service X that has a single purpose - help to integrate two existing services (A and B) having different domain models. Some sort of adapter pattern.
There are cases when A wants to call B, and cases when B wants to call A.
How should endpoints of X be named to make clear for which direction each endpoint is meant?
For example, let's assume that the service A manages "apples". And the service B wants to get updates on "apples".
The adapter service X would have two endpoints:
PUT /apples - when A wants to push updated "apples" to B
GET /apples - when B wants read "apples" from A
(without awaiting a push from A)
Such endpoint structure as above is quite misleading. The endpoints are quite different and use different domain models: PUT-endpoint awaits model of A, and GET-endpoint return model of B.
I would appreciate any advice on designing the API in such a case.
I don't like my own variant:
PUT /gateway-for-A/apples
GET /gateway-for-B/apples
In my view, it is fine, but can be improved:
PUT /gateway-for-A/apples
GET /gateway-for-B/apples
Because
forward slashes are conventionally used to show the hierarchy between individual resources and collections: /gateway-for-A/apples
What can be improved:
it is better to use lowercase
remove unnecessary words
So I would stick with the foloowing URI:
PUT /a/apples
GET /b/apples
Read more here about Restful API naming conventions
First things first: REST has no endpoints, but resources
Next, in terms of HTTP you should use the same URI for updating the state of a resource and retrieving updates done to it as caching, which basically uses the effective URI of a resource, will automatically invalidate any stored responses for an URI if a non-safe operation is performed on it and forward the request to the actual server. If you split concerns onto different URIs you basically bypass that cache management performed for you under the hood completely.
Note further, HTTP/0.9, HTTP/1.0 and HTTP/1.1 itself don't have a "push" option. It is a request-response protocol and as such if a client is interested in getting updates done to a resource it should poll the respective resource whenever it needs updates. If you need above-mentioned push though you basically need to switch to Web Sockets or the like. While HTTP/2 introduced server push functionality, this effectively just populates your local 2nd level cache preventing the client from effectively requesting the resource and instead using the previously received and cached one.
Such endpoint structure as above is quite misleading. The endpoints are quite different and use different domain models: PUT-endpoint awaits model of A, and GET-endpoint return model of B.
A resource shouldn't map your domain model 1:1. Usually in a REST architecture there can be way more resources than there are entities in your domain model. Just think of form-like resources that explain a client on how to request the creation or update of a resource or the like.
On the Web and therefore also in REST architectures the representation format exchanged should be based on well-defined media-types. These media types should define the syntax and semantics of elements that can be found within an exchanged document of that kind. The elements in particular provide the affordance that tell a client in particular what certain elements can be used for. I.e. a button wants to be pressed while a slider can be dragged left or right to change some numeric values or the like. You never have to frequent any external documentation once support for that media type is added to your client and/or server. A rule of thumb in regards to REST is to design the system as if you'd interact with a traditional Web page and then apply the same concepts you used for interacting with that Web page and translate it onto the REST application domain.
Client and server should furthermore use content-type negotiation to negotiate which representation format the server should generate for responses so that clients can process them. REST is all about indirections that ultimately allow a server to change its internals without affecting clients that behave well negatively. Staying interoperable whilst changing is an inherent design decision of REST. If that is not important to you, REST is probably overkill for your needs and you probably should use something more (Web-) RPC based.
In regards to you actual question, IMO a messaging queue could be a better fit to your problem than trying to force your design onto a REST architecture.
I was hoping that there is a well-known pattern for adapter service (when two different services are being integrated without knowing each other formats)
I'd compare that case with communication attempts among humans stemming from different countries. I.e. imagine a Chines who speaks Mandarin trying to communicate with a Frech. Either the Chinese needs to be able to talk French, the French being able to talk in Mandarin, they both use an intermediary language such as English or they make use of a translator. In terms of trust and cost, the latter option might be the most expansive one of all of these. As learning laguages though is a time-consuming, ongoing process this usually won't happen quickly unless special support is added, by hiring people with that language skills or using external translators.
The beauty of REST is, servers and clients aren't limited to one particular representation format (a.k.a. language). In contrast to traditional RPC services, which limit themselves to one syntax, in REST servers and clients can support a multitude of media types. I.e. your browser knows how to process HTML pages, how to render JPG, PNG, GIF, ... images, how to embed Microsoft Word, Excel, ... documents and so forth. This support was added over the years and allows a browser to basically render a plethora of documents.
So, one option is to either create a translation service that is able to translate one representation to an other and then act as middleman in the process or you directly add support for the non yet understood media types to the service/client directly. The more media-types your client/servers are able to process, the more likely they will be to interoperate with other peers in the network.
The former approach clearly requires that the middleman service is able to at least support the two representation formats issued by A and B but on the other hand allows you to use services not directly under your control. If at least one of the services though is under your control, directly adding the not-yet-supported media type could be potentially less work in the end. Especially when certain library support for the media type is already available or can be obtained easily.
In a REST architecture clients and servers aren't build with the purpose of knowing the other one by heart. This is already a hint that there is a strong coupling between these two. They shouldn't be aware of the others "API" other than that they use HTTP as transport layer and URIs as their addressing scheme. All other stuff are negotiated and discovered on the fly. If they don't share the same language capabilities the server will responde with a 406 Not Accepttable response that informs a client that they don't speak the same languages and thus won't be able to communicate meaningfully.
As mentioned before, REST is all about introducing indirections to aid in the decoupling intent which allows servers to evolve freely in future without those changes breaking clients as these will just coop with the change. Therefore, eventual change in future is an inherent design concept. If at least one participant in a REST architecture doesn't respect these design concepts they are a potential candidate for introducing the problems traditional RPC services did in the past, like breaking clients on a required change, maintaining v2/3/4/.../n of various different APIs and scaling issues due to the direct coupling of client and servers.
Not sure why you need to distinguish it in the path and why the domain or subdomain is not enough for it:
A: PUT x.example.com/apples -> X: PUT b.example.com/apples
B: GET x.example.com/apples -> X: GET a.example.com/apples
As of the model, you want to do PUSH-PULL in a system which is designed for REQ-REP. Let's translate the upper: pushApples(apples) and pullApples() -> apples if this is all they do, then PUT and GET are just fine with the /apples URI if you ask me. Maybe /apples/new is somewhat more expressive if you need only the updates, but I would rather use if-modified-since header instead and maybe push with if-unmodified-since.
Though I think you should describe what the two service does, not what you do with the apples, which appear to be a collection of database entities instead of a web resource, which is several layers above the database. Currently your URIs describe the communication, not the services. For example why is X necessary, why don't they call each other directly? If you can answer that question, then you will understand what X does with the apples and how to name its operations and how to design the URIs and methods which describe them.

Rest api with generic User

I created a few Rest apis right now and I always preferred a solution, where I created an endpoint for each resource.
For example:
GET .../employees/{id}/account
GET .../supervisors/{id}/account
and the same with the other http methods like put, post and delete. This blows up my api pretty much. My rest apis in general preferred redundancy to reduce complexity but in this cases it always feels a bit cumbersome. So I create another approach where I work with inheritance to keep the "dry" principle.
In this case there is a base class User and via inheritance my employee and supervisor model extends from it. Now I only need one endpoint like
GET .../accounts/{id}
and the server decides which object is returned. Also while this thins out my api, it increases complexity and in my api documentation ( where I use spring rest docs ) I have to document two different Objects for the same endpoint.
Now I am not sure about what is the right way to do it ( or at least the better way ). When I think about Rest, I think in resources. So my employees are a seperate resource as well as my supervisors.
Because I always followed this approach, I tink I might be mentally run in it and maybe lost the objectivity.
It would be great if you can give my any objective advice on how this should be handled.
I built an online service that deals with this too. It's called Wirespec:
https://wirespec.dev
The backend automatically creates the url for users and their endpoints dynamically with very little code. The code for handling the frontend is written in Kotlin while the backend for generating APIs for users is written in Node.js. In both cases, the amount of code is very negligible and self-maintaining, meaning that if the user changes the name of their API, the endpoint automatically updates with the name. Here are some examples:
API: https://wirespec.dev/Wirespec/projects/apis/Stackoverflow/apis/getUserDetails
Endpoint: https://api.wirespec.dev/wirespec/stackoverflow/getuserdetails?id=100
So to answer your question, it really doesn't matter where you place the username in the url.
Try signing in to Wirespec with your Github account and you'll see where your Github username appears in the url.
There is, unfortunately, no wright or wrong answer to this one and it soley depends on how you want to design things.
With that being said, you need to distinguish between client and server. A client shouldn't know the nifty details of your API. It is just an arbitrary consumer of your API that is fed all the information it needs in order to make informed choices. I.e. if you want the client to send some data to the server that follows a certain structure, the best advice is to use from-like representations, such as HAL forms, Ion or even HTML. Forms not only teach a client about the respective properties a resource supports but also about the HTTP operation to use, the target URI to send the request to as well as the representation format to send the data in, which in case of HTML is application/x-www-form-urlencoded most of the time.
In regards to receiving data from the server, a client shouldn't attempt to extract knowledge from URIs directly, as they may change over time and thus break clients that rely on such a methodology, but rely on link relation names. Per URI there might be multiple link relation names attached to that URI. A client not knowing the meaning of one should simply ignore it. Here, either one of the standardized link relation names should be used or an extension mechanism as defined by Web linking. While an arbitrary client might not make sense from this "arbitrary string" out of the box, the link relation name may be considered the predicate in a tripple often used in ontologies where the link relation name "connects" the current resource with the one the link relation was annotated for. For a set of URIs and link relation names you might therefore "learn" a semantic graph over all the resources and how they are connected to each other. I.e. you might annotate an URI pointing to a form resource with prefetch to hint a client that it may load the content of the referenced URI if it is IDLE as the likelihood is high that the client will be interested to load that resource next anyway. The same URI might also be annotated with edit-form to hint a client that the resource will provide an edit form to send some data to the server. It might also contain a Web linking extension such as https://acme.org/ref/orderForm that allows clients, that support such a custom extension, to react to such a resource accordingly.
In your accounts example, it is totally fine to return different data for different resources of the same URI-path. I.e. resource A pointing to an employee account might only contain properties name, age, position, salery while resource B pointing to a supervisor could also contain a list of subordinates or the like. To a generic HTTP client these are two totally different resources even though they used a URI structure like /accounts/{id}. Resources in a REST architecture are untyped, meaning they don't have a type ouf of the box per se. Think of some arbitrary Web page you access through your browser. Your browser is not aware of whether the Web page it renders contains details about a specific car or about the most recent local news. HTML is designed to express a multitude of different data in the same way. Different media types though may provide more concrete hints about the data exchanged. I.e. text/vcard, applciation/vcard+xml or application/vcard+json all may respresent data describing an entity (i.e. human person, jusistic entity, animal, ...) while application/mathml+xml might be used to express certain mathematical formulas and so on. The more general a media type is, the more wiedspread usage it may find. With more narrow media types however you can provide more specific support. With content type negotiation you also have a tool at your hand where a client can express its capabilities to servers and if the server/API is smart enough it can respond with a representation the client is able to handle.
This in essence is all what REST is and if followed correctly allow the decoupling of clients from specific servers. While this might sound confusing and burdensome to implement at first, these techniques are intended if you strive for a long-lasting environment that still is able to operate in decateds to come. Evolution is inherently integrated into this phiolosophy and supported by the decoupled design. If you don't need all of that, REST might not be the thing you want to do actually. Buf if you still want something like REST, you for sure should design the interactions between client and server as if you'd intereact with a typical Web server. After all, REST is just a generalization of the concepts used on the Web quite successfully for the past two decades.

RESTful syntax. Is it Eager/Lazy or both?

I am trying to follow RESTful principles and a little confused on how "Eager" or "Lazy" endpoints would be set up.
For example, a
Shop has many Products
Products have many Ingredients.
Products have many Packging
Of course a "bad" endpoint that would fetch eagerly would be:
api/shop/1
Would return shop id 1's details but also with:
ALL the Products
ALL the Product's Ingredients
ALL the Product's Packging
This of course is a crazy model...so I can only guess RESTful is "always lazy" by default?
But with "lazy be default" say you want to get 10 different products AND their ingredients...
api/shop/1/product/1/ingredients
api/shop/1/product/2/ingredients
api/shop/1/product/3/ingredients
The number of requests is getting a little high...10 seperate HTTP requests for the 10 products.
So lastly, do you instead tend to design the RESTful endpoints based on what the front-end/consumer may want as opposed to modelling the business/database?
api/shop/1/product-details?productId=1,2,3,4,5,6,7,8,9,10
Is the above strictly "RESTful"?
So I guess the real underlying question is sort of:
Is RESTful API design a model of the Data or a model of the Views?
Is RESTful API design a model of the Data or a model of the Views?
Views is closer -- it's a model of resources
Your data model is not your object model is not your resource model is not your affordance model. -- Amundsen
The simplest analogy is to look at java script in an HTML page
we can embed the java script in the HTML page
we can link to the java script from the HTML page.
Both approaches work - they have different trade offs, primarily in how caching works.
Coarse grained resources are somewhat analogous to data transfer objects; exchange a large representation in a single request/response, and then the client can do lots of different things with that one representation.
Fine grained resources give you more control of caching strategies (the different parts can expire at different times), and perhaps respond better to scenarios where we expect the client to be sending back edited representations of those resources.
One issue that fine grained resources have had is the extra burden of round trips. HTTP/2 improves that story, as server push can be used to chain representations of multiple resources onto a single response -- all of the fine grained resources can be sent in a single burst.
But even so, we're talking about identifying resources, not database entities.
https://stackoverflow.com/questions/57420131/restful-syntax-is-it-eager-lazy-or-both
That's an identifier for a web page about a question
https://api.stackexchange.com/2.2/questions/57420131?site=stackoverflow
That's a different resource describing the same question.
REST API's aren't about exposing your data model via HTTP, they are about exchanging documents so that a client can navigate a protocol that gets useful work done. See Webber 2011.

Struggling with REST and Larman's (RCP?) system operations

In Craig Larman's book Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development (3rd Edition), a use case is converted to a System Sequence Diagram (SSD) which contains system operations. It's a way of doing high-level design first, that's easily traceable to the requirements (use case), and later refining it by detailing the system operations at the domain level via OO design principles.
I'm trying to understand this methodology using RESTful services. The trouble spots seem to be with resources and the stateless operations.
Here's a sample from the book, an SSD with four system operations:
An SSD is also an abstraction of the presentation layer (on the left) and the domain layer (on the right). The domain layer might also include the application and or business logic.
In Larman's approach, a system operation (e.g., makeNewSale()) should be handled by a GRASP Controller which is a domain non-presentation-layer object that handles the system operation.
Now, let's say we're trying to use RESTful services in this approach:
The Uniform Interface constraint of REST is part of the presentation layer. For example, one can configure the route that a POST operation on a resource takes, allowing it to eventually call some operation of an object in the Domain layer. This Byte Rot blog by Aliostad explains very well the details of traditional layer architectures and REST.
In Larman's example, the Cashier clicks somewhere in the GUI to invoke a http://...com/sales - POST request and eventually a GRASP Controller receives makeNewSale(). The REST part http://...com/sales - POST request creates a new resource for the sale e.g., 001 and also makeNewSale() is sent to a GRASP controller object.
Session Controllers (mentioned in the GRASP Controller pattern) don't exist on the System, since REST operations are stateless on the server. This implies that system operations might actually need more arguments to pass state information to the server, and that isn't obvious from the use case. For example, makeNewSale() probably needs to receive and process an authentication token as an argument, since :System has no session information. I found a related question and answers here at RESTful Authentication
So, assuming my understanding up to now is right, my questions are:
Does the Uniform Interface constraint of REST respect loose coupling of presentation/domain layer separation? At first, it seems to be a detail of the presentation layer (like an actionPerformed() method in Java Swing). But the part that bothers me is that http://...com/sales ties right into a Sale (which is a domain) object.Another way of putting it: by creating REST resources and REST verbs accessing them via a Uniform Interface, isn't application/business logic being put into to the presentation layer? Larman's SSD approach is about layers and he explicitly states that application logic should not go in the presentation layer. For example, a POST to http://...com/sales creates a new sales/001 resource and also sends makeNewSale(). The first part seems like business logic. The fact that my REST resource names follow many of my Domain object names seems like my Presentation layer is (more) coupled to my Domain layer, than if I were using Swing and only called makeNewSale() from an actionPerformed() on a JButton.
Larman's SSD concept is oriented towards the RCP model (with state in the server), but can an SSD be made to easily jibe with a REST philosophy? For example, endSale() exists to break the system out of a loop of enterItem() calls. Later in the book, Larman even presents a state diagram about this:
I read about How to manage state in REST and it seems one has to take care with passing state on each system operation if needed (as in the authentication token example above), or the "state" is actually encapsulated in the resources. I thought about it, and with REST, endSale() could probably be removed entirely.
I used a concrete example here, so that the answers can also be concrete.
I'll try to address your questions, but keep in mind that this is a very much debatable topic..
The REST Uniform Interface does respect the separation of concerns between presentation and business logic. The separation is about the presentation layer not knowing details about the implementation of the business logic and vice-versa, but there is no point in hiding what can be done with a domain entity (i.e. the services that are available for a resource). What you want to hide is how the actions are executed.
The REST's HATEOAS principle dictates that the representation of a resource (the instance of a domain entity) should encapsulate the state of the client/server interaction, so the services should return that state to the client one way or another. This, and the use of hypermedia (i.e. links to available services - representing actions that can be done on a resource, given its state) should map quite easily to the state diagram you posted.
Let's continue with your example :
let's say that there is a service newSale returning a Sale object. This service must be invoked using the POST method. in the Sale's hypermedia section there are 2 links, one to the addItem service (which is also a POST and accepts an updated version of the Sale object, returning it after it has been saved), and one to the endSale service (also a POST), which saves the Sale and marks it as 'complete'. This last service does not return any resource representation, just the http response OK if the object is succesfully saved.

iOS Client: "Caching" Server-side data to persistent storage

I'm building an iOS client app to interface with an existing backend architecture. To reduce latency, API calls and payloads, it'd be nice to "cache" model data client-side for faster indexing and then make updates to both client/server sides accordingly as needed.
The current theoretical stack would look something like this:
Server Side >>>>>>>>>>>>>>>>> Client Side
-----------------------------------------
PHP >> JSON >> CORE DATA >> UIKit Objects
NOTE: It's also worth noting that the iOS client, while itself adhering to MVC internally would in essence be a "View" in a larger MVC client-server architecture. Thus, just like one updates the model after a user action or updates the view after a model change, the server would need to sync with a client change and the client would need to sync with a server-side change.
Some Context:
A. Many diverse data structures may be coming over the pipe and would have to be constructed into UIViews dynamically. A schema will likely have to be defined (I'm not sure if there's a "best way" to adhere to a JSON schema client-side other than remembering what the acceptable object structures are). I've realized the need to separate model data pertaining to the creation of custom views ("View" Models) from model data of what will be presented in those views ("Regular" Models).
B. End-users should be able to immediately CRUD (create, read, update, destroy) most data presented in these views (but not CRUD the views themselves). They may later need to view this in a web interface or other context.
C. RestKit looks like a good candidate for getting from the API to JSON to COREDATA. I need to find out if it structurally supports callbacks when client model copies need to be pushed to the server. Perhaps the best way is noting in the client model when a change has occurred and notifying whatever RestKit-based HTTP manager to pass it along to the server.
Ultimate Question:
Can anyone speak to best practices, pitfalls, tips, and frameworks with this type of architecture? (Particularly when it comes to performance and the distribution of work between client and server, but general advice is also much appreciated.)
I've done some work around this, hopefully I can provide some insight.
In regards to A) Yes if you're planning to use CoreData (or RestKit) you'll need to know you schema up front. You'll have no way to map dynamic objects otherwise unless you have some generic object type where you're just stashing the JSON string or something, but this doesn't sound like what you're trying to do since you mention users editing those objects.
B) RestKit will handle pushing to the server for you, but you'll still want some control over this I imagine. We handled it by always saving locally first then pushing up to the server on a successful save. This also enabled us to work when there's no network. You'll just have to handle the edge cases of what happens when the server rejects the update / create / delete your user is performing.
C) RestKit will likely get you 80% of the way there, as it did for us. Just having something to understand REST endpoints and object mapping, and abstracting the HTTP requests was a huge help. In terms of the system understanding changes, we kept a flag on managed objects as to whether the object needed a sync or not. We could fetch based on those flags and push the server up.
One thing with RestKit is that you can have other attributes in your CoreData model that aren't necessarily a part of the JSON schema, but you might need within your app. For instance I already mentioned the flag for knowing whether an object needed sync. We also kept pre-computed fields that we used to search on and some other random pieces of information for determining the order of objects to push up to the server (dependencies).
Hope this helps. If you have more specific questions I might have more answers.